Archiwum kategorii: CSS

What Does a Well-Documented CSS Codebase Look Like?

Post pobrano z: What Does a Well-Documented CSS Codebase Look Like?

In the front-end community, there is a lot of attention related to documenting JavaScript. That’s not so much the case with CSS. Often times I feel like lost when I join a project with minimal or no CSS documentation.

Even though CSS is relatively easy to write, it can be quite hard to maintain. The specificity, the global scope of everything, and the lack of guidance can easily lead to inconsistency, code duplication, and over-complication.

I’ve long been curious what a really well-documented CSS codebase looks like. Here, I’ll share my experience, along with the expectations I have towards my vision of well-documented stylesheets.

It surprises me where I hear people say that commenting CSS is not that important. I imagine none of them have had to deal with 10,000+ line stylesheets! Often I’ve struggled with what HTML results in what specific style. Without having a solid context about the development decisions taken, debugging efforts increase. WTFs per minute increase exponentially too.

Many times I’ve spent hours to figure out what the developer intended, why she didn’t do it the another way, why is this layout seemingly so complex. There is much pain buried in these „why” questions.

So, here we go! Let’s examine the 4 big signs of a well-documented CSS codebase.

1) CSS Tech Stack & Toolchain

There are likely things as part of our CSS code base like third-party libraries, mixins, or other tools. Looking in the package manager’s dependencies list doesn’t give a lot of context why the decisions were made to add these things, what they do exactly, and how we’re using them.

It would be good for everyone to know why a certain library or tool was introduced. Imagine, for example, that a third-party library was introduced only to solve what has become an obsolete CSS issue. If we had context like that, we could make more informed decisions.

There sometimes can be quite a few third-party libraries in a project. Have you ever spent a bunch of time on web searching each one figuring out what it even is? It can be quite a challenge to know or to keep track of what each exactly does.

A well-documented codebase would include a description for each dependency. Perhaps you could the comment tweet-length (140 characters) explaining why it is there. That would give anyone else in the code base a head start on why something is there.

I like adding these descriptions right in the place where I @import them.

2) CSS Conventions

Good coding conventions result in consistent, readable, and unambiguous source code. They standardize the structure and coding style of an application so that you and others can easily read and understand the code.

It’s important to know if there are any project-specific naming conventions or methodologies in place (like BEM, OOCSS, SMACSS, or ACSS). I’ve seen cases where a certain methodologies principles are applied but the actual rules followed are modified based on the preferences of the developers involved. So an indication how strictly we must follow the methodology principles would be good to leave in a well-documented code base.

This brings up the larger issue of CSS style guides. A naming convention is just one choice as part of a complete styling strategy. Other parts might be:

All this makes up a complete CSS style guide. Having a shared vocabulary like this I’d consider a strong plus towards consistency.

3) CSS Architecture

Most scalable projects follow some type of architecture in terms of ordering styles. In a well-documented codebase, the fundamental principles that the project follows when structuring and sectioning styles should be mentioned.

I was first inspired to explore CSS architecture by watching Harry Roberts’s talk about managing CSS projects. Here’s Harry:

CSS architecture seems to be somewhat in vogue right now. It’s something you’ve no doubt heard mentioned numerous times over the past year or so, and with good reason: UIs (and the teams that build them) are getting bigger and more complicated than ever before.

There are a number of aspects of CSS which make it troublesome. It’s declarative, meaning there is no logic or control flow to tell other developers much about the state or construction of the project. It operates in a global namespace, meaning we get collisions, leaking styles and inadvertent regressions. It utilizes inheritance, making everything somewhat interdependent and brittle. Finally, the unavoidable specificity model can cause problems when selectors fight each other for prominence.

Therefore, he introduces a concept for architecting CSS called ITCSS. If you are working on a project of reasonable scale, chances are someone already defined similar principles or ideas that aim to resolve these problems. So in a well-documented codebase, I would expect to see them written somewhere.

One could tell if the architecture is explained well enough if you can answer the following question: Where should new styles or stylesheets be added?

4) CSS Component Descriptions and Examples

A common pattern is to separate the logical modules into CSS components (or „blocks” according to BEM). Some of them might be re-usable, some may not, but the important thing is that they are the building blocks of our project. Therefore, describing what they are should be a top priority in a well-documented codebase.

Ideally, you should arrange and group them, name them, and establish rules between them to generate an overview of all components. A well-described CSS component doesn’t only include information about what the component does but also has other valuable intel like example HTML markup and the context in which it’s meant to be used. Going one step further brings up the issue of Pattern Libraries. A pattern library is a collection of reusable components that can be used together to create a website. With modular, component-based architecture becoming a trend, they can bring huge value.

The goal of a pattern library is to show what can be built with existing patterns (components). But let’s take a look at what additional information can be displayed alongside each pattern also. Vitaly Friedman shared a good summary on how taking the pattern library to the next level looks like. He states that focusing on components isn’t good enough:

One of the main problems with pattern libraries is that, while they provide an overview of components, they often leave a lot open to interpretation. Components can be combined in a variety of ways, consistently and inconsistently. It’s great to be able to see what button variants and iconography are available and what kinds of tables and pricing tags one may use, but what if you need to design or build an interface that contains all of these components at once — and perhaps another that doesn’t yet exist in the library?

A list of modules alone wouldn’t convey any context or any specifics on how the modules should (and should not) be used.

Based on Vitaly’s post and Brad Frost’s anatomy of a pattern in a pattern library, here are a few ideas I can imagine each of our patterns (components) could have, despite the usual unique name, code sample and a description of the component’s purpose. Basic (fundamental):

  • Tags or categories: The assigned tags or categories for the component. Developers could tag their components with „in use”, „needs refactoring” tags and etc.
  • Responsive preview: A real-life, resizable preview of the component, using the actual snippet of code being used in production. Alternatively, just a screenshot.
  • Versioning and legacy, team members involved or responsible: In a bigger team, the ownership of the (family of) components and which team members have been actively developing them could be really helpful for maintenance and further development.

… and here are a few more advanced:

  • Performance impact: Sometimes CSS could be heavy too. An indicator of performance or a „warning signs” section, outlining not only performance impact but also any common mishaps when the pattern is used incorrectly.
  • Accessibility implications: Indicator of accessibility requirements. Some components might require more work to maintain accessibility, especially if they interact with other components.
  • Related patterns: A quick overview of related components or the family of components that a given component belongs to. Could use an explanation of when to use a component, when not to, and why.
  • Fallback and print previews.

… the list goes on and on to whatever makes sense for your specific use-case.

Conclusion

A well-documented CSS codebase enforces consistency, boosts maintainability, and helps the team to build a shared vocabulary. It is a prerequisite for efficient CSS design and development. Moreover, based on my experience, it inevitably leads to better performance. I strongly believe that these are the signs of the professional execution of a project.

If you have thoughts, please feel free to add them in the comments below, so together we can come closer to better documentation practices.


What Does a Well-Documented CSS Codebase Look Like? is a post from CSS-Tricks

Connect: behind the front-end experience

Post pobrano z: Connect: behind the front-end experience

Some fantastic behind-the-scenes stuff about Stripe’s design work by Benjamin De Cock. Absolutely everything is clever and using very modern techniques.

Plus one I’d never seen before:

Connect’s landing page uses the new Intersection Observer API which provides a much more robust and performant way to detect the visibility of an element … The observeScroll helper simplifies our detection behavior (i.e. when an element is fully visible, the callback is triggered once) without executing anything on the main thread.

Direct Link to ArticlePermalink


Connect: behind the front-end experience is a post from CSS-Tricks

An Introduction to the `fr` CSS unit

Post pobrano z: An Introduction to the `fr` CSS unit

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

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

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

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

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

The syntax is essentially this:

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

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

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

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

We’ve inadvertently caused some horizontal scrolling here:

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

This is where the fr unit can help us.

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

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

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

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

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

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

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

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

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

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

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

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

Information from other folks

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

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


Rachel Andrew has a video specifically about fr:


Anna Monus has a very good article on fr.


Yay for the fr unit!


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

A Little Example of Data Massaging

Post pobrano z: A Little Example of Data Massaging

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

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

We had fallen into the classic WordPress trap

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

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

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

It’s not WordPress’ fault

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

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

The structure we needed

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

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

Even that’s not perfect

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

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

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

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

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

In ACF, our „Field Group” ended up like this:

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

Let the data massaging begin

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

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

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

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

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

Design is a lot easier with clean and structured data

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

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


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

CSS-Tricks Chronicle XXXI

Post pobrano z: CSS-Tricks Chronicle XXXI

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


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


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


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


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


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


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


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

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


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

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


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


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

CSS Animations vs Web Animations API

Post pobrano z: CSS Animations vs Web Animations API

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

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

A note on browser support

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

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

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

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

The basics of WAAPI

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

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

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

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

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

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

Recreating an animista CSS animation with WAAPI

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

The actual perf is much better than this GIF.

Here’s those keyframes in CSS:

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

Here’s the same code in WAAPI:

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

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

element.animate(keyframes, 700);

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

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

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

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

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

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

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

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

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

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

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

Easing

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

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

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

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

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

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

Ease vs Linear

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

Performance

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

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

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

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

WAAPI Versus CSS Animations?

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

Animation Object

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

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

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

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

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

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

getAnimations()

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

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

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

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

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

Now we can use the web animation API on our CSS animation 🙂

myCSSAnimation.playbackRate = 4;
myCSSAnimation.reverse();

Promises and Events

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

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

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

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

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

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

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

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

The Future

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


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

Fun with Viewport Units

Post pobrano z: Fun with Viewport Units

Viewport units have been around for several years now, with near-perfect support in the major browsers, but I keep finding new and exciting ways to use them. I thought it would be fun to review the basics, and then round-up some of my favorite use-cases.

What are viewport units?

Four new „viewport-relative” units appeared in the CSS specifications between 2011 and 2015, as part of the W3C’s CSS Values and Units Module Level 3. The new units – vw, vh, vmin, and vmax – work similarly to existing length units like px or em, but represent a percentage of the current browser viewport.

  • Viewport Width (vw) – A percentage of the full viewport width. 10vw will resolve to 10% of the current viewport width, or 48px on a phone that is 480px wide. The difference between % and vw is most similar to the difference between em and rem. A % length is relative to local context (containing element) width, while a vw length is relative to the full width of the browser window.
  • Viewport Height (vh) – A percentage of the full viewport height. 10vh will resolve to 10% of the current viewport height.
  • Viewport Minimum (vmin) – A percentage of the viewport width or height, whichever is smaller10vmin will resolve to 10% of the current viewport width in portrait orientations, and 10% of the viewport height on landscape orientations.
  • Viewport Maximum (vmax) – A percentage of the viewport width or height, whichever is larger10vmin will resolve to 10% of the current viewport height in portrait orientations, and 10% of the viewport width on landscape orientations. Sadly, and strangely, vmax units are not yet available on Internet Explorer or Edge.

While these units are derived from viewport height or width, they can all be used everywhere lengths are accepted – from font-size to positioning, margins, padding, shadows, borders, and so on. Let’s see what we can do!

Responsive Typography

It’s become very popular to use viewport units for responsive typography – establishing font-sizes that grow and shrink depending on the current viewport size. Using simple viewport units for font-size has an interesting (dangerous) effect. As you can see, fonts scale very quickly – adjusting from unreadably small to extra large in a very small range.

This direct scaling is clearly too dramatic for daily use. We need something more subtle, with minimums and maximums, and more control of the growth rate. That’s where calc() becomes useful. We can combine a base size in more steady units (say 16px) with a smaller viewport-relative adjustment (0.5vw), and let the browser do the math: calc(16px + 0.5vw)

See the Pen partially-Responsive Type by Miriam Suzanne (@mirisuzanne) on CodePen.

By changing the relationship between your base-size and viewport-relative adjustment, you can change how dramatic the growth-rate is. Use higher viewport values on headings, and watch them grow more quickly than the surrounding text. This allows for a more dynamic typographic scale on larger screens, while keeping fonts constrained on a mobile device – no media-queries required. You can also apply this technique to your line-height, allowing you to adjust leading at a different rate than the font-size.

body {
  // font grows 1px for every 100px of viewport width
  font-size: calc(16px + 1vw);
  // leading grows along with font,
  // with an additional 0.1em + 0.5px per 100px of the viewport
  line-height: calc(1.1em + 0.5vw);
}

For me, this is enough complexity. If I need to constrain the top-end for rapid-growth headings, I can do that with one single media-query wherever the text becomes too large:

h1 {
  font-size: calc(1.2em + 3vw);
}

@media (min-width: 50em) {
  h1 {
    font-size: 50px;
  }
}

Suddenly I wish there was a max-font-size property.

Others have developed more complex calculations and Sass mixins to specify the exact text-size ranges at specific media-queries. There are several existing CSS-Tricks articles that explain the technique and provide snippets to help you get started:

I think that’s overkill in most cases, but your milage will absolutely vary.

Full-Height Layouts, Hero Images, and Sticky Footers

There are many variations on full-height (or height-constrained) layouts – from desktop-style interfaces to hero images, spacious designs, and sticky footers. Viewport-units can help with all of these.

In a desktop-style full-height interface, the page is often broken into sections that scroll individually – with elements like headers, footers, and sidebars that remains in place at any size. This is common practice for many web-apps these days, and vh units make it much simpler. Here’s an example using the new CSS Grid syntax:

See the Pen Full-height CSS Grid by Miriam Suzanne (@mirisuzanne) on CodePen.

A single declaration on the body element, height: 100vh, constrains your application to the height of the viewport. Make sure you apply overflow values on internal elements, so your content isn’t cut off. You can also achieve this layout using flexbox or floats.Note that full-height layouts can cause problems on some mobile browsers. There’s a clever fix for iOs Safari, that we use to handle one of the most noticeable edge-cases.

Sticky-footers can be created with a similar technique. Change your body height: 100vh to min-height: 100vh and the footer will stay in place at the bottom of your screen until it’s pushed down by content.

See the Pen Sticky-Footer with CSS Grid by Miriam Suzanne (@mirisuzanne) on CodePen.

Apply vh units to the height, min-height, or max-height of various elements to create full-screen sections, hero images, and more. In the new OddBird redesign, we constrained our hero images with max-height: 55vh so they never push headlines off the page. On my personal website, I went with max-height: 85vh for a more image-dominated look. On other sites, I’ve applied min-height: 90vh to sections.

Here’s an example showing both a max-height heroic kitten, and a min-height section. Combining all these tricks can give you some powerful control around how your content fills a browser window, and responds to different viewports.

Fluid Aspect Ratios

It can also be useful to constrain the height-to-width ratio of an element. This is especially useful for embeded content, like videos. Chris has written about this before. In the good-old-days, we would do that with %-based padding on a container element, and absolute positioning on the inner element. Now we can sometimes use viewport units to achieve that effect without the extra markup.

If we can count on the video being full-screen, we can set our height relative to the full viewport width:

/* full-width * aspect-ratio */
.full-width {
  width: 100vw;
  height: calc(100vw * (9/16));
}

That math doesn’t have to happen in the browser with calc. If you are using a pre-processor like Sass, it will work just as well to do the math there: height: 100vw * (9/16). If you need to constrain the max-width, you can constrain the max-height as well:

/* max-width * aspect-ratio */
.full-width {
  width: 100vw;
  max-width: 30em;
  height: calc(100vw * (9/16));
  max-height: calc(30em * (9/16));
}

Here’s a demonstration showing both options, with CSS custom properties (variables) to make the math more semantic. Play with the numbers to see how things move, keeping the proper ratio at all times:

See the Pen Fluid Ratios with Viewport Units by Miriam Suzanne (@mirisuzanne) on CodePen.

Chris takes this one step farther in his pre-viewport-units article, so we will too. What if we need actual HTML content to scale inside a set ratio – like presentation slides often do?

We can set all our internal fonts and sizes using the same viewport units as the container. In this case I used vmin for everything, so the content would scale with changes in both container height and width:

See the Pen Fluid Slide Ratios with Viewport Units by Miriam Suzanne (@mirisuzanne) on CodePen.

Breaking the Container

For years now, it’s been popular to mix constrained text with full-width backgrounds. Depending on your markup or CMS, that can become difficult. How do you break content outside of a restricted container, so that it fills the viewport exactly?

Again, viewport units can come in handy. This is another trick we’ve used on the new OddBird site, where a static-site generator sometimes limits our control of the markup. It only takes a few lines of code to make this work.

.full-width {
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}

There are more in-depth articles about the technique, both at Cloud Four and here on CSS Tricks.

Getting Weird

Of course, there’s much more you can do with viewport units, if you start experimenting. Check out this pure CSS scroll-indicator (made by someone named Mike) using viewport units on a background image:

See the Pen CSS only scroll indicator by Mike (@MadeByMike) on CodePen.

What else have you seen, or done with viewport units? Get creative, and show us the results!


Fun with Viewport Units is a post from CSS-Tricks

Creating Yin and Yang Loaders On the Web

Post pobrano z: Creating Yin and Yang Loaders On the Web

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

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

Animated gif. The yin and yang symbol is rotating while its two lobes alternate increasing and decreasing in size - whenever one is increasing, it squishes the other one down.
The desired result: a rotating ☯ symbol, with its two lobes increasing and decreasing in size.

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

The static yin and yang symbol.
The static yin and yang symbol (live demo).

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

The structure of the yin and yang symbol. The two lobes are circular arcs (half circles) whose radii are half the radius of the big circle enclosing the symbol. The two small circles are in the middle of the two lobes and their diameters are half of those of the half circle lobes.
The structure of the static symbol (live demo).

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

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

Plain old HTML + CSS

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

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

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

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

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

$d: 80vmin;

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

So far, so good:

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

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

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

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

Next, we need to give them different backgrounds:

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

Now we’re getting somewhere!

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

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

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

However, the result doesn’t look quite right:

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

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

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

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

$f: .5;
$t: 1s;

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

@keyframes s { to { transform: scale(2 - $f) } }

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

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

The last step is to make the whole symbol rotate:

$t: 1s;

.☯ {
  /* same styles as before */
  animation: r 2*$t linear infinite;
}

@keyframes r { to { transform: rotate(1turn) } }

And we got the final result!

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

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

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

From the above, it results that:

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

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

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

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

Canvas + JavaScript

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

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

$d: 80vmin;

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: lightslategray;
}

canvas {
  width: $d; height: $d;
  border-radius: 50%;
  background: white;
}

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

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

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

const _C = document.querySelector('canvas'), 
      CT = _C.getContext('2d');

let r;

function size() {
  _C.width = _C.height = Math.round(_C.getBoundingClientRect().width);
	
  r = .5*_C.width;
	
  CT.translate(r, r);
};

size();

addEventListener('resize', size, false);

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

Illustration showing how half of the main shape of the symbol is made up of three half circle arcs. The first arc is half a circle following the contour of the symbol's outer circle shape, clockwise from -180° to 0°. The second one is another half a circle of half the radius of the first, going clockwise from the point where the previous arc started, 0° on its smaller support circle to 180° on the same circle. The third one is another half circle, going anticlockwise from the point where the previous arc ended, 0° on its support circle, to -180° on its support circle.
The structure of the three arc shape (live demo).

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

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

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

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

CT.closePath();
CT.fill();

The result can be seen in the following Pen:

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

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

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

After adding this arc, our shape becomes:

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

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

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

We now have the shape we wanted:

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

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

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

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

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

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

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

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

We now have the static symbol!

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

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

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

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

Initial vs. final state of the animation. The initial state shows the first lobe (second arc of the three arc shape) shrunken to the minimum possible (its radius being F*.5*r, where F is a value between 0 and 1), while the other lobe has expanded to fill the remaining state. In the final state, things are reversed: the first lobe has expanded, while the second one has shrunk.
The initial vs. the final state of the animation (live demo).

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

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

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

const F = .5;

let rmin, rmax;

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

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

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

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

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

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

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

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

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

const T = 120;

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

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

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

So our animation function becomes:

const T = 120;

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

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

This gives us the initial state of the animation:

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

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

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

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

CT.fillStyle = 'black';

Now we can actually start the animation:

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

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

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

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

const T = 120, A = 2*Math.PI/T;

(function ani(t = 0) {
  let k = .5*(1 + Math.cos(t*A));

  /* same as before */
})();

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

CT.rotate(A);

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

SVG + JavaScript

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

$d: 80vmin;

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: lightslategray;
}

svg {
  width: $d; height: $d;
  border-radius: 50%;
  background: white;
}

This gives us a white disc:

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

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

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

- var r = 1500;

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

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

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

Let’s take a closer look at the components of an „arc to” (A) command:

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

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

Illustration showing how half of the main shape of the symbol is made up of three half circle arcs. The first arc is half a circle following the contour of the symbol's outer circle shape, clockwise from (-r,0) to (r,0). The second one is another half a circle of half the radius of the first, going clockwise from the point where the previous arc started to (0,0). The third one is another half circle of the same radius as the previous one, going anticlockwise from the point where the previous arc ended to (-r,0).
The structure of the three arc shape with coordinates of arc endpoints (live demo).

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

- var r1 = .5*r, r2 = r - r1;

path(d=`M${-r} 0
        A${r} ${r} 0 0 1 ${r} 0
        A${r1} ${r1} 0 0 1 ${r - 2*r1} 0
        A${r2} ${r2} 0 0 0 ${-r} 0`)

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

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

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

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

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

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

We now have the static shape!

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

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

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

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

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

This gives us the morphing shape:

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

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

let ca = t*A;

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

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

SVG + CSS

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

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

- var r = 1500;

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

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

- var r = 1500;

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

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

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

$r: 1500;
$r1: .5*$r;
$r2: $r - $r1;

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

This gives us our three arcs shape:

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

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

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

We now have the static shape:

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

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

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

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

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

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

$t: 1s;

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

And the shape morphing part works!

Animated gif. Shows the three arcs shape morphing from the state where the first lobe is the minimal one to the state where the second one is minimal.
The three arcs shape morphing (live demo, Blink only).

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

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

We set this animation on the circle elements:

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

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

Animated gif. Here, the radii of the two small circles are also animated from their minimum to their maximum size and back.
The three arcs shape morphing and the small circles scaling (live demo, Blink only).

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

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

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

Animated gif. The radii of the two small circles are also animated from their minimum size to their maximum size and back such that, when the first is at its minimum, the second is at its maximum and the other way around.
The three arcs shape morphing and the small circles scaling (live demo, Blink only).

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

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

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

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

@keyframes x {
    0% { cx: calc(var(--j)*#{$rmax}) }
  100% { cx: calc(var(--j)*#{$rmin}) }
}

The result can be seen below… and it’s not quite as expected:

Animated gif. The position of the two small circles should animate smoothly, instead it seems to flip suddenly from the initial one to the final one.
Result (live demo, Blink only).

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

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

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

@keyframes x {
    0% { transform: translate($rmax*1px) }
  100% { transform: translate($rmin*1px) }
}

This finally behaves as we wanted it to!

Animated gif. The position of the small circles animates smoothly between the initial and the final one for each.
Correct animation (live demo, Blink only)

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

path {
  d: path('M#{-$r} 0A#{$r} #{$r} 0 0 1 #{$r} 0' +
          'A#{$rmin} #{$rmin} 0 0 1 #{$r - 2*$rmin} 0' + 
          'A#{$rmax} #{$rmax} 0 0 0 #{-$r} 0');
  animation: m $t ease-in-out infinite alternate
}

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

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

@keyframes s { to { r: $rmax/3 } }

@keyframes x { to { transform: translate($rmin*1px) } }

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

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

svg { animation: r 2*$t linear infinite }

@keyframes r { to { transform: rotate(1turn) } }

The finished loading animation can be seen in this Pen.

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


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

Breaking Out with CSS Grid Explained

Post pobrano z: Breaking Out with CSS Grid Explained

Tyler Sticka shared a slick technique for breaking out content in a CSS Grid layout, but Rachel Andrew goes the extra mile to explain why the technique works:

When you name lines, you can optionally name them as *-start and *-end and this gives you a little more grid magic. We get a named grid area of the main name used. Sounds odd? Take a look at the diagram below, it shows 4 named grid lines, main-start and main-end both for columns and rows. The area marked out by the intersection of these lines can now be referenced by the name main. If we had named the lines foo-start and foo-end then we would have a named area called foo.

Rachel’s post stood out to me for a number of reasons. First, I love blog posts as responses to other blog posts. Second, it’s an excellent reminder that sharing how a concept works is equally as important as showing that it works. Lastly, the concept of implicitly named grid areas based on named grid lines is as good a reason as any to roll up our sleeves and get cozy with the spec. In fact, following Rachel’s series on the CSS Grid spec is a good starting point.

As a side note, Tyler’s clever use of named Grid lines reminded me of Dave Rupert’s equally crafty use of :not to achieve a similar full bleed effect.

Direct Link to ArticlePermalink


Breaking Out with CSS Grid Explained is a post from CSS-Tricks

Using Filters in Vue.js

Post pobrano z: Using Filters in Vue.js

Filters are an interesting way to deal with data rendering in Vue but are only useful in a small amount of cases. The first thing to understand about filters is that they aren’t replacements for methods, computed values, or watchers, because filters don’t transform the data, just the output that the user sees. As of Vue 2.0, there are no built-in filters, we need to construct them ourselves.

We can use filters locally or globally, but it’s worth mentioning that if you declare a Vue filter globally it should come before the Vue instance. In both cases, we would pass the value in as a parameter.

//global
Vue.filter('filterName', function(value) {
  return // thing to transform
});
 
//locally, like methods or computed
filters: {
  filterName(value) {
    return // thing to transform
  }
}

Filters are used with a pipe, following the piece of data you’d like to be altered upon render. So we would show the piece of data we want to alter, followed by the filter

{{ data | filter }}

Here’s a small example, with a tip calculator:

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

new Vue({
  el: '#app',
  data() {
    return {
      customer1total: 35.43    
    }
  },
  filters: {
    tip15(value) {
      return (value*.15).toFixed(2)
    },
    tip20(value) {
      return (value*.2).toFixed(2)
    },
    tip25(value) {
      return (value*.25).toFixed(2)
    }
  }
});
<div id="app">
  <h2>Tip Calculator</h2>
  <p><strong>Total: {{ customer1total }}</strong></p>
  <p>15%: {{ customer1total | tip15 }}</p>
  <p>20%: {{ customer1total | tip20 }}</p>
  <p>25%: {{ customer1total | tip25 }}</p>
</div>

You can also use filters in v-bind directives rather than just the mustache template. Filters can also be chained. Keep in mind if you’re going to chain filters: ordering matters. The first filter will be applied first, the second will be applied to the completed first, and so on.

 {{ data | filterA | filterB }}

We can also pass additional arguments into filters like so:

{{ data | filterName(arg1, arg2) }}
// locally, like methods or computed
filters: {
  filterName(value, arg1, arg2) {
    return //thing to transform
  }
}

Now, you might think, based on the name, that filters would be great for forms when we want to show only some bits of data and not others. However, filters need to rerun on every single update, so if you have something like an input that updates every time you type, it’s not very performant. Better to use computed for something like this as it’s less overhead. The results will be cached based on their dependencies and won’t be rerun on every update. Computed properties will only be reevaluated when those dependencies change, but can also handle complex logic. This makes them excellent candidates for filtering information based on input. There are, however, circumstances where you do need to update based on changes in time, and for these instances, better to use a method.


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