Tag: Making

Making width and flexible items play nice together

The short answer: flex-shrink and flex-basis are probably what you’re lookin’ for.

The long answer

Let’s say you want to align an image and some text next to each other with like this:

A small image of a yellow illustrated bird to the left of a block of text.

Now let’s say you reach for flexbox to make it happen. Setting the parent element to display: flex; is a good first start.

.container {    display: flex;  }

And this results in…

See the Pen
Flex-Shrink Example 1
by Robin Rendle (@robinrendle)
on CodePen.

Yikes! Well, that’s kinda okay, I guess. It makes sense that the image would bump right up against the text like that because we haven’t set a width on the image. Ideally, though, we’d like that image to have a fixed width and then the text should take up whatever space is left over.

Okay, so let’s go do that!

.container {    display: flex;  }  img {    width: 50px;   margin-right: 20px;  }

See the Pen
Flex-Shrink Example 2
by Robin Rendle (@robinrendle)
on CodePen.

This looks great in Chrome. But wait, what? If we inspect the image tag in Firefox DevTools, we’ll find that it’s not the width value that we set at all:

We could use min-width to force the image to the 50px width we want:

img {   min-width: 50px;   margin-right: 20px; }

Buuuuuuut, that only sets helps with the width so we’ve got to put a margin in as well.

img {   min-width: 50px;   margin-right: 20px; }

There we go. That’s better in Firefox and still works in Chrome.

The even longer answer

I realized the image is getting the squished treatment because we need to use the flex-shrink property to tell flex items not to decrease in size, regardless of whether or not they have a width.

All flex-items have a flex-shrink value of 1. We need to set the image element to 0:

.container {    display: flex;  }  img {   width: 50px;   margin-right: 20px;   flex-shrink: 0; }

See the Pen
Flex-Shrink Example 3
by Robin Rendle (@robinrendle)
on CodePen.

Getting better! But we can still do more to improve this.

The director’s cut answer

We can tidy things up further because flex-shrink is included in the flex shorthand property.

flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

If we set the flex-shrink value to 0 and the flex-basis value to the default width we want the image to be, then we can get rid of the width property altogether.

.container {    display: flex;  }  img {   flex: 0 0 50px;   margin-right: 20px; }

Oh yeah:

See the Pen
Flex-Shrink Example 2
by Geoff Graham (@geoffgraham)
on CodePen.

Another example

That flex-shrink property solves a ton of other problems and is pretty dang important if you want to start using flexbox. Here’s another example why: I stumbled upon yet another problem like the one above and I mentioned it in a recent edition of the newsletter. I was building a navigation component that would let users scroll left and right through multiple items. I noticed the following problem when checking my work:

See the Pen
flex-shrink nav item 1
by Robin Rendle (@robinrendle)
on CodePen.

That longer navigation item shouldn’t break into multiple lines like that — but I finally understood why this was happening, thanks to the previous issue. If you set the flex-shrink property to 0 then it will tell each item in this navigation not to shrink and instead assume the width of the content instead, like this:

See the Pen
flex-shrink nav item
by Robin Rendle (@robinrendle)
on CodePen.

And, yes, we can go the extra step once again to use the flex property instead, this time using auto as the flex-basis since we want the maximum amount of space for all items to be considered when divvying up space in the navigation container.

See the Pen
Setting flex for flexible nav elements
by Geoff Graham (@geoffgraham)
on CodePen.

Huzzah! We figured it out. Even though the answer is a single line of code, it’s is pretty essential one to making truly flexible elements.

CSS-Tricks

, , , , , ,

Material Theming: Making Material Your Own!

The web is a beautiful, expressive medium that’s evolved over time as trends and technology have changed. Moments of delight and flair are what set companies apart from one another. At the same time, today’s top products rely on scalable, component-based design systems to efficiently develop a coherent brand. And it’s more important than ever to have accessibility and a solid user experience baked in from the start! But these two worlds — creative web design and systematic web design — don’t need to be at arms. The beautiful gooey center of the web design world is where we can find a way to meld creative web design in with our systems, and luckily, you can have both!

One of those design systems is called Material Design (the team I just joined at Google!). Material is built on years of research and best practices to give designers and developers access to creating beautiful, accessible UIs with the least amount of work possible. But in its initial launch, one thing was missing from the equation: the ability to express a brand’s personality in an easy-to-implement way. The team heard two key pieces of feedback:

  1. Material wasn’t stylistically flexible enough to meet the needs of all products, brands, and users.
  2. It wasn’t easy enough to apply and build brand experiences systematically across products.

And as of last year (specifically, I/O 2018), Material announced new theming capabilities! Theming allows developers and designers to benefit from all the parts of Material that make it a world-class design system — and make it their own! In other words: You can customize the look and feel of Material Components by applying global changes to your product’s color, shape, and typography.

Material Components are built for multiple systems like Android, iOS, and Flutter, and we’re going to focus on the web for this post (this is CSS-Tricks after all). Let’s start off with a base login template using Material Components for the web to see how easy implementing a theme atop this will be:

About theming

The current theming implementations allow for color, typography, and shape adjustments that trickle down to every component in your product. These three subsystems may not sound like many options, but together they bring a big impact to the design, and are a great springboard to jump off of with more design changes on a more granular level.

Color

The first themable option is color. There’s a great color picker tool on Material.io for you to be able to see contrast and aid your color selection process. Since this is CSS-Tricks, let’s write some CSS (okay, more like SCSS), to get started with an example. In a few lines of code in our my-theme.scss file, we can entirely change the look and feel of this login screen:

We’ve set:

$ mdc-theme-primary: #26418f; $ mdc-theme-secondary: #d1c4e9; $ mdc-theme-background: #fdf6f9;

Even though we have not specified a $ mdc-theme-on-primary, the system knows to make this white now (using a Sass contrast function) to help us ensure accessible color contrast. We can also override the text color on the primary and secondary themes with $ mdc-theme-on-primary and $ mdc-theme-on-secondary. However, if no value is explicitly set, these will either be black or white based on the background. See the color picker for more information.

Typography

Material sets up a base typography scale, which can be customized with theming. You can adjust the typography for each individual headline level and apply it throughout your product in two ways:

  1. Using CSS classes to apply the styles. I.e. <h1 class="mdc-typography--headline1">
  2. Using mixins to extend the styles from a header component onto another (i.e. h1 {@include mdc-typography(headline1);}). You can see an example of this in our starter kit example on Glitch.

A quick way to change the typeface is by using Google Fonts — a really nice directory of free web fonts. We’re going to make this easy for ourselves and pick out a Google font called Josefin Sans. In order to use this in our project, we’ll need to import it, and then can set the base typography to use it:

@import url('https://fonts.googleapis.com/css?family=Josefin+Sans'); $ mdc-typography-font-family: unquote("Josefin Sans, sans-serif");

We use unquote here because the typography in Material is set in a map. We can specify more styles like so:

$ mdc-typography-styles-button: (  font-size: 14px,  font-weight: 600,  letter-spacing: 0.05em, );

Now our login page looks like this:

Shape

Shape is another way to be expressive with your code! What do I mean by shape? In Material, shape affects the corner radius of components, such as buttons, cards, and sheets. In product design, shape helps to define your brand. Is it angular and machine-like, or is it more rounded and organic? Changing shape trickles down through the rest of the system. Here are some examples of shape in Material:

The other cool thing about shape in Material Components is that you don’t have to have the same shape on each corner, or even the same component at different sizes. There are small, medium, and large components in Material, and you can apply different styles for these different component types, and you can mix it up and specify each corner, one-by-one, in a space-separated list of values just as you would with border-radius.

MDC Web does not currently support cut corners like the other platforms do since it is a non-trivial operation with current web standards. This is another reason I personally love Houdini and see a lot of impact for it in future CSS development!

In the example we’re working with, we have both large and small components. The large components are the text field inputs, and the small component is the “login” button. We can apply different shapes to these with:

$ mdc-shape-small-component-radius: 12px 4px; $ mdc-shape-large-component-radius: 8px;

And voilà! We have transformed our application in 12 lines of code.

Themes, themes, everywhere!

Now here’s the part where you get creative. There are so many ways to mix and match the elements of color, typography, and shape to really make your brand stand out. Here are four very distinct example apps that use color theory, typography, and shape to differentiate their products, each based on the baseline Material Components:

Themes IRL

Google itself customizes and extends upon the baseline Material Components with its own products. This is called the Google Material Theme, and it was defined by a number of product teams, including Gmail, Google News, Google Play, and Google Home:

But they are not the only company to use Material Design and its principles. A few companies have also extended Material Components in their own large-scale products.

Lyft, a ridesharing service, is just one example. They used an extended FAB component to highlight key actions, and designed it with a gradient to give it a unique feel that users were still familiar with. Lyft also leveraged Material’s elevation system in their own product.

Anchor, a free app that helps people record, distribute, and host podcasts, is another great example of making Material your own. They showcase their bold color palette, and integrate it with choice chips to delineate display options, a list of cards for content selection, and an extended FAB anchored to the bottom to remain persistently available for user actions.

Get started

Now you have everything you need to make own Material Theme with Material Components for the web. It’s easy to start building your web project with our Material Starter Kit, and exploring theming with the Build a Material theme Glitch project we’ve just released. Change around the variables in my-theme.scss to explore how they can customize the individual components. Then, you can open any template, paste those variables into the my-theme.scss, and start building.

TL;DR: If you thought that Material Design wasn’t for you in the past, it’s a great time to start exploring. Happy theming!

The post Material Theming: Making Material Your Own! appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Making the Move from jQuery to Vue

As someone who has used jQuery for many. years and has recently become a Vue convert, I thought it would be an interesting topic to discuss the migration process of working with one to the other.

Before I begin though, I want to ensure one thing is crystal clear. I am not, in any way whatsoever, telling anyone to stop using jQuery. That’s been pretty fashionable lately, and heck, I wrote up something similar myself a few year ago (“How I’m (Not) Using jQuery”). If you get things done with jQuery and your end users are successfully using your site, then more power to you. Keep using what works for you.

This guide is more for people who may be coming from years of jQuery experience and want to see how things can be done with Vue. With that in mind, I’m going to focus on what I consider “core” jQuery use cases. I won’t cover every single possible feature but instead take a “I often did [X] with jQuery” approach that may be more relatable to people considering learning Vue. (As an aside, also note that how I write my examples are simply one way of performing a task. Both jQuery and Vue give provide multiple ways to accomplish the same goal and that’s a great thing!)

With that in mind, let’s consider some high level things we might turn to jQuery for:

  • Finding something in the DOM (with the idea of doing something with it later)
  • Changing something in the DOM (e.g. the text of a paragraph or the class of a button)
  • Reading and setting form values
  • Form validation (which is really just a combination of the items above)
  • Ajax calls and handling the results
  • Event handling (e.g. on button click, do something)
  • Measuring or changing the styles of an element

There’s more to jQuery, of course, but these uses (at least in my opinion), cover the most common use cases. Also note there’s a lot of cross pollination in the list above. So, should we start with a simple one-to-one comparison of each? Nope, not so fast. Let’s begin by covering the major difference in Vue applications.

Defining where Vue “works”

When we drop jQuery onto a page, we are basically adding a Swiss Army knife to JavaScript code to handle common web development tasks. We can do any of the uses case we’re going to cover in whatever order we see fit. For example, a client may ask for form validation today, then in a month or so, ask to do an Ajax-based search form in the header of the site.

Vue has one significant difference in that respect. When starting a with Vue project, we start by defining an “area” in the DOM we want it to focus on. So, let’s consider a simple prototype web page:

<body>    <header>     Fancy header stuff here   </header>    <div id="sidebar">     Some sidebar doohicky here   </div>    <main>     <p>       Main site content here, super important stuff...     </p>     <div id="loginForm">       A login form of course     </div>   </main>  </body>

In a typical jQuery application, we may write code to work with the header, sidebar, and login form or something. No big whoop:

$  (document).ready(function() {    $  ('header') // something...    $  ('#sidebar') // something...    $  ('#loginForm') // something...   });

In a Vue application, we first specify what are we’re working with. Imagine our client first asked to add validation to the loginForm element. Our Vue code would specify that:

new Vue({   el: '#loginForm',   // Code here... });

This means that we’d typically end up adding a second Vue application if the client later decides to have us add something to the sidebar:

new Vue({   el:'#loginForm',   // Code here... });  new Vue({   el:'#sideBar',   // Code here... });

Is that a bad thing? Absolutely not. Right away, we get the benefit of encapsulation. If we accidentally use a variable with a generic name (we’ve all done that), we don’t have to worry about conflicts with other parts of your code. Later on when the client adds yet another request, having our unique, logical sets of Vue code separated out like this gives us some great peace of mind that things won’t step on each other.

So, yes, a good thing. But it absolutely caused me to stop a bit when I first began using Vue. Now, onto our use cases.

Finding Stuff in the DOM

Another aspect you’ll find interesting, or scary, is how to “find stuff in the DOM.” That’s a bit vague, but let’s consider a firm example. We have a button, and when it’s clicked, we something to happen. Here’s an abbreviated example of how this could look:

<button id="myButton">Click Me!</button> <!-- More stuff... --> <script> $  (document).ready(function() {    $  ('#myButton').click(function() {     alert(1);   });  }); </script>

Now let’s compare that to how it can be done with Vue:

<div id="app">   <button v-on:click="doSomething">Click Me!</button> </div>  <script> const app = new Vue({   el:'#app',   methods: {     doSomething: function() {       alert(1);     }   } }); </script>

The Vue application is a bit more verbose, but note how the markup has a direct connection between the action (“click”) and the function that will be called. Vue’s code doesn’t have a tie back to the DOM (outside of the el portion where we define where it needs to work). This was easily one of the things that sold me on Vue the most — it feels easier to tell what is going on. Also, I didn’t need to worry so much about the ID value and selectors. If I change the class or ID of the button, I don’t need to go back into my code and worry about updating selectors.

Let’s consider another example: finding and changing text in the DOM. Imagine that button, on click, now changes the text of another part of the DOM.

<button id="myButton">Click Me!</button> <span id="result"></span>  <!-- More stuff... -->  <script> $  (document).ready(function() {    $  ('#myButton').click(function() {     $  ('#result').text('You clicked me, thanks!');   });  }); </script>

I’ve added a new span and now, when the button is clicked, we use another selector to find it and use a jQuery utility method to change the text inside it. Now consider the Vue version:

<div id="app">   <button v-on:click="doSomething">Click Me!</button>   <!-- On click, change text in this span -->   <span>{{resultText}}</span> </div>  <script> const app = new Vue({   el: '#app',   data: {     resultText: ''   },   methods: {     doSomething: function() {       this.resultText = 'You clicked me, thanks!';     }   } }); </script>

In this example, we’re using Vue’s template language (the highlighted line) to specify that we want to render a variable inside the span, which is resultText in this case. Now, when the button is clicked, we change that value and the span’s inner text will change automatically.

As an aside, Vue supports a shorthand for the v-on attribute, so the button in the example could have been written with @click="doSomething" instead.

Reading and writing form variables

Working with forms is probably one of the most common — and useful — things that we can do with JavaScript. Even before JavaScript, most of my early “web development” was writing Perl script to handle form submissions. As the primary way of accepting user input, forms have always been critical to the web and that’s probably going to stay the same for quite some time. Let’s consider a simple jQuery example of reading a few form fields and setting another:

<form>   <input type="number" id="first"> +    <input type="number" id="second"> =   <input type="number" id="sum">    <button id="sumButton">Sum</button> </form>  <script> $  (document).ready(function() {   let $  first = $  ('#first');   let $  second = $  ('#second');   let $  sum = $  ('#sum');   let $  button = $  ('#sumButton');      $  button.on('click', function(e) {     e.preventDefault();     let total = parseInt($  first.val(),10) + parseInt($  second.val(),10);     $  sum.val(total);   }); }); </script>

This code demonstrates how jQuery can both read and write via the val() method. We end up getting four items from the DOM (all three form fields and the button) and use simple math to generate a result. Now consider the Vue version:

<form id="myForm">   <input type="number" v-model.number="first"> +    <input type="number" v-model.number="second"> =   <input type="number" v-model="sum">    <button @click.prevent="doSum">Sum</button> </form>  <script> new Vue({   el: '#myForm',   data: {     first: 0,     second: 0,     sum: 0   },   methods: {     doSum: function() {       this.sum = this.first + this.second;     }   } }) </script>

This introduces some interesting Vue shortcuts. First, v-model is how Vue creates two way data binding between values in the DOM and in JavaScript. The data block variables will automatically sync up with the form fields. Change the data, and the form updates. Change the form, and the data updates. The .number is a flag to Vue to treat the inherit string values of form fields as numbers. If we leave this off and do addition as we are, we’ll see string additions and not arithmetic. I’ve been working with JavaScript for nearly a century and still screw this up.

Another neat “trick” is @click.prevent. First, @click defines a click handler for the button, then the .prevent portion blocks the browser’s default behavior of submitting the form (the equivalent of event.preventDefault()).

The final bit is the addition of the doSum method that’s bound to that button. Note that it simply works with the data variables (which Vue makes available in the this scope).

While this is mostly my personal feeling here, I really love the lack of query selectors in the script when writing in Vue and how the HTML is much more clear about what it’s doing.

Finally, we could even get rid of the button completely:

<form id="myForm">   <input type="number" v-model.number="first"> +    <input type="number" v-model.number="second"> =   <input type="number" v-model="sum">  </form>  <script> new Vue({   el: '#myForm',   data: {     first: 0,     second: 0   },   computed: {     sum: function() {       return this.first + this.second;     }   } }) </script>

One of the cooler features of Vue is computed properties. They are virtual values that recognize when their derived values are updated. In the code above, as soon as any of the two form fields change, the sum will update. This works outside of form fields too. We could render the sum like so:

The total is {{sum}}.

Working with Ajax

It’s commendable how easy jQuery has made it to use Ajax. In fact, I can say I’ve done Ajax “the vanilla” way probably a grand total of one time. (If you’re curious, you can take a look at the spec for XMLHttpRequest and probably be happy you avoided it yourself.) jQuery’s simple $ .get(...) method worked in a large number of cases and when it’s needed for something more complex, $ .ajax() made it easy as well. Another thing jQuery did well is the way it handles JSONP requests. While mostly unnecessary now with CORS, JSONP was a way to handle making requests to APIs on different domains.

So, what does Vue do for you to make Ajax easier? Nothing!

OK, that sounds scary but it really isn’t. There are many options out there for working with HTTP requests, and Vue.js took a more agnostic route of letting us, the developers, decide how we want to handle it. So yes, that does mean a bit more work, but we’ve got some great options.

The first one to consider is Axios, this is a Promise-based library that is very popular among the Vue community. Here’s a simple example of it in action (taken from their README file):

axios.get('/user?ID=12345')   .then(function (response) {     // handle success     console.log(response);   })   .catch(function (error) {     // handle error     console.log(error);   })   .then(function () {     // always executed   });

Axios supports POST requests, of course, and lets us specify headers among many other options.

While Axios is very popular among Vue developers, it isn’t something that really clicked with me. (At least not yet.) Instead, I’ve been much more a fan of Fetch. Fetch is not an external library but is a web standard way of performing HTTP requests. Fetch has very good support at roughly 90% of browsers, though that means it isn’t completely safe to use, but we can always use a polyfill we need to.

While it’s totally out of the scope of what we’re discussing here, Kingsley Silas has written an excellent guide on using both Axios and Fetch with React.

Like Axios, Fetch is Promise-based and has a friendly API:

fetch('http://example.com/movies.json')   .then(function(response) {     return response.json();   })   .then(function(myJson) {     console.log(JSON.stringify(myJson));   });

Both Axios and Fetch cover all types of HTTP requests, so either will fit an any number of needs. Let’s look at a simple comparison. Here’s a simple jQuery demo that makes use of the Star Wars API.

<h1>Star Wars Films</h1> <ul id="films"> </ul>  <script> $  (document).ready(function() {   $  .get('https://swapi.co/api/films', function(res) {     let list = '';     res.results.forEach(function(r) {       list += `<li>$  {r.title}</li>`;     });     $  ('#films').html(list);   }); }); </script>

In the sample above, I use $ .get to hit the API and return a list of films. Then I generate a list of titles as li tag elements with that data and insert it all into a ul block.

Now, let’s consider an example of this using Vue:

<div id="app">   <h1>Star Wars Films</h1>   <ul>     <li v-for="film in films">{{film.title}}</li>   </ul>   </div>  <script> const app = new Vue({   el: '#app',   data: {     films: []   },    created() {      fetch('https://swapi.co/api/films')     .then(res => res.json())     .then(res => {       this.films = res.results;       });   } }) </script>

Probably the best part of this is the use of the v-for template. Notice how Vue isn’t concerned with the layout (well, at least the JavaScript). The data is fetched from the API. It’s assigned a variable. The layout handles displaying it. I’ve always hated having HTML in my JavaScript and, while solutions exist for that with jQuery, having it baked into Vue makes it a natural fit.

A full (if somewhat trivial) example

To bring it home a bit, let’s consider a more real world example. Our client has asked us to build a fancy Ajax-enabled front-end search interface to a product API. The feature list includes:

  • Support filtering by name and product category
  • Form validation such that we must supply a search term or a category
  • While the API is being hit, show a message to the user and disable the submit button
  • When done, handle reporting that no products were shown or list the matches

Let’s begin with the jQuery version. First, the HTML:

<form>   <p>     <label for="search">Search</label>     <input type="search" id="search">   </p>   <p>     <label for="category">Category</label>     <select id="category">       <option></option>       <option>Food</option>       <option>Games</option>     </select>   </p>    <button id="searchBtn">Search</button> </form>  <div id="status"></div> <div id="results"></div>

There’s a form with our two filters and two divs. One’s used as a temporary status when searching or reporting errors and one is used to render results. Now, check out the code.

const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';  $  (document).ready(() => {   let $  search = $  ('#search');   let $  category = $  ('#category');   let $  searchBtn = $  ('#searchBtn');   let $  status = $  ('#status');   let $  results = $  ('#results');      $  searchBtn.on('click', e => {     e.preventDefault();          // First clear previous stuff     $  status.html('');     $  results.html('');      // OK, now validate form     let term = $  search.val();     let category = $  category.val();     if(term === '' && category === '') {       $  status.html('You must enter a term or select a category.');       return false;     }      $  searchBtn.attr('disabled','disabled');     $  status.html('Searching - please stand by...');          $  .post(productAPI, { name:term, category:category }, body => {       $  searchBtn.removeAttr('disabled');       $  status.html('');        if(body.results.length === 0) {         $  results.html('<p>Sorry, no results!</p>');         return;       }              let result = '<ul>';       body.results.forEach(r => {         result += `<li>$  {r.name}</li>`       });       result += '</ul>';       $  results.html(result);     });        }); });

The code begins by creating a set of variables for each of the DOM items we want to work with — the form fields, button, and divs. The core of the logic is within the click handler for the button. We do validation, and if everything is OK, do a POST request against the API. When it returns, we either render the results or show a message if nothing was matched.

You can work with a complete version of this demo using the CodePen below.

See the Pen
jQuery Full
by Raymond Camden (@cfjedimaster)
on CodePen.

Now let’s consider the Vue version. Again, let’s start with the layout:

<div id="app">   <form>     <p>       <label for="search">Search</label>       <input type="search" v-model="search">     </p>     <p>       <label for="category">Category</label>       <select v-model="category">         <option></option>         <option>Food</option>         <option>Games</option>       </select>     </p>     <button @click.prevent="searchProducts" :disabled="searchBtnDisabled">Search</button>   </form>      <div v-html="status"></div>     <ul v-if="results">       <li v-for="result in results">{{result.name}}</li>     </ul> </div>

From the top, the changes include:

  • Wrapping the layout in a div that can be used to let Vue know where to work.
  • Using v-model for the form fields to make it easy to work with the data.
  • Using @click.prevent to handle doing the main search operation.
  • Using :disabled to bind whether or not the button is disabled to a value in the Vue application (we’ll see that in action in a moment).
  • The status value is a bit different than earlier examples. While jQuery has a specific method to set text in a DOM item and another for HTML, Vue requires using v-html when assigning HTML to a value that’s going to be rendered. If we tried to do {{status}} with HTML, the tags would be escaped.
  • Finally, using v-if to conditionally render a list of results along with v-for to handle the iteration.

Now let’s look at the code.

const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';  const app = new Vue({   el: '#app',   data: {     search: '',     category: '',     status: '',     results: null,     searchBtnDisabled: false   },   methods: {     searchProducts:function() {       this.results = null;       this.status = '';              if(this.search === '' && this.category === '') {         this.status = 'You must enter a term or select a category.';         return;       }        this.searchBtnDisabled = true;       this.status = 'Searching - please stand by...';              fetch(productAPI, {         method: 'POST',         headers: {           'Content-Type':'application/json'         },         body: JSON.stringify({name:this.search,category:this.category})       }).then(res => res.json())       .then(res => {         this.status = '';         this.searchBtnDisabled = false;         this.results = res.results;         if(this.results.length === 0) this.status = '<p>Sorry, no results!</p>';       });            }   } });

The first block worth calling out is the set of data fields. Some map to form fields and others to results, status messages, and the like. The searchProducts method handles much of the same stuff as the jQuery version but, in general, there’s much less code directly tied to the DOM. For example, even though we know the results are listed in an unordered list, the code itself doesn’t worry about that. It simply assigns the value and the markup handles rendering it. Overall, the JavaScript code is much more concerned about logic in comparison to the jQuery code which “feels” like a much nicer separation of concerns.

As before, I’ve got a CodePen for you to try this out yourself:

See the Pen
Vue Full
by Raymond Camden (@cfjedimaster)
on CodePen.

Death to jQuery! Long Live Vue!

OK, that’s a bit over the top. As I said in the beginning, I absolutely think that you shouldn’t change a thing if like working with jQuery and it’s working for you.

I can say, however, that Vue feels like a great “next step” for people who are used to working with jQuery. Vue supports complex applications and has a great command line tool for scaffolding and building projects. But for simpler tasks, Vue works as a great “modern jQuery” replacement that has become my tool of choice for development!

For another perspective on using Vue in place of jQuery, check out Sarah Drasner’s “Replacing jQuery With Vue.js: No Build Step Necessary” because it includes a handful of other super helpful examples.

The post Making the Move from jQuery to Vue appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Making Web Components for Different Contexts

This article isn’t about how to build web components. Caleb Williams already wrote a comprehensive guide about that recently. Let’s talk about how to work with them, what to consider when making them, and how to embrace them in your projects.

If you are new to web components, Caleb’s guide is a great read, but here are more resources that will get you up to speed:

Since web components are now widely supported (thanks to the hard work of many people behind the scenes) — and considering the imminent switch that Edge will make to the chromium platform — people are now thinking about web components as “native” and a platform-compliant way to build reusable UI components to keep consistency across design systems and web projects, while using the power of the Shadow DOM to encapsulate style and logics inside the component itself.

Well, this can be true and false at the same time. But first, let’s get acquainted with the Abstraction Layers Triangle.

The Abstraction Layers Triangle

Technically, we should consider web components as an extension of our favorite markup language, HTML (yep!). The Web Components API allows us to create custom HTML elements (e.g. <foo-bar>) that don’t exist in HTML.

We are told web components are basically new HTML elements, so we should consider them as part of the HTML specifications and, consequently, we should follow its paradigms, core concepts, and utilization. If we assume all of these points, we will figure out that our components will live among the lowest levels of the web platform stack, alongside HTML, CSS, and JavaScript.

Frameworks and libraries like React, Vue, Angular, SvelteJS work on their abstraction level, right above other tools that live in a sort of “middle earth,” like StencilJs, Hybrids and Lit. Under these layers of abstraction, we can find our basic web technologies… and vanilla web components. We can represent this concept with an ALT (A>bstraction L Triangle) diagram:

The higher we go, the more abstraction things get.

Why is this important? Well, it helps us visualize the various layers that exist on top of native components and understand the context they are used so that they can be built for an intended context. What’s context? That’s where we’re headed.

Same technology, different contexts

The Shadow DOM is a key factor in the Web Components API. It allows us to bundle JavaScript and CSS inside a custom element to both prevent external interferences and style manipulations, unless we expressly allow it. There are indeed some approaches that developers can follow to allow external CSS to leak into the shadow root and into a component, including custom properties and the ::part and ::theme pseudo-elements, which is something Monica Dinculescu) has covered so well.

There is also another thing to consider: the context we are working with. After three years of building web components personally, I can identify two contexts: the private context (like a design system) and the standard context (like plain HTML, CSS, and JavaScript without custom styles).

Before designing components, we need to know how they will be used, so determining the type of context is a key to all of this. The most easy way is targeting only one context, but with a small CSS trick. we can build our components for both of them.

Let’s look at the differences between the two contexts before we get into that.

Private context

A private context is a closed ecosystem that provides components with their own style that must be used as-is. So, if we are building a component library that comes from specific styling guidelines or a design system, each component will reflect custom styles so there’s no need to code them up each time they’re needed.

That can be true also with JavaScript logic. For example, we can attach a closed shadow root that prevent others to accidentally pierce the shadow boundary with a querySelector. As a a result, we can simply pick and use all any component, avoiding issues like style inconsistencies and CSS collisions. As the author, you can also get to define a set of CSS custom properties (or ::parts) that can be used to style a component for a specific use case, but this is not the focus point of a design system.

Here’s an example of a web component component in a private context. It has all of the styles and logic contained inside its shadow-root and and can simply be dropped into any page.

See the Pen
Closed Context Web Component
by Mattia Astorino (@equinusocio)
on CodePen.

This example and the one to follow are for demonstration purposes and not intended for production use because they do not make considerations for key situations, like accessibility and other optimizations.

Components in a private context can be rarely used outside of that context. For example, if we try to take an element from a design system (which has its own enforced styles), we’re unable to simply add it to a project and expect to customize it. You know how Bootstrap can be themed and customized to your liking? This is pretty much the opposite of that. These components are made to live inside their context and their context only.

Standard context

A standard context may be the most complex type of component, not only because the environment can range anywhere from a full-fledged framework (like Vue and React) to plain vanilla HTML, but also because everyone should be able to use that component like any other element.

For example, when adding a component publicly, say to npm, those who use it will expect to be able to customize it, at least to some degree.

Do you know of any HTML element that comes with its own presentational style? The answer should be no because, well, elements must be explicitly styled with CSS. This is also true for web components made in a standard context. A single web component should be customizable by adding classes an attributes, or other methods.

Here’s the same <todo-list> element that we saw in the closed context example, but designed for a standard context. It works as-is, without any presentational styles inside its shadow root. In fact, it only contains the required logic and baseline CSS to make sure it functions. Otherwise, it’s completely customizable like any standard HTML element, like a div.

See the Pen
Standard Context Web Component
by Mattia Astorino (@equinusocio)
on CodePen.

Both of the examples we’ve looked at for each context are made with the same component. The difference is that the component in a standard context an be customized and selected with external CSS.

Web components and composition

OK, so working with web components is really the same as working with plain HTML, though as we’ve seen, it’s important to follow the paradigms and principles of the given content. Well, thing we need to be mindful of is the web component composition.

As explained by Google Web Fundamentals:

Composition is one of the least understood features of shadow DOM, but it’s arguably the most important.

In our world of web development, composition is how we construct apps, declaratively out of HTML. Different building blocks (<div>s, <header>s, <form>s, <input>s) come together to form apps. Some of these tags even work with each other. Composition is why native elements like <select>, <details>, <form>, and <video> are so flexible. Each of those tags accepts certain HTML as children and does something special with them. For example, <select> knows how to render option> and <optgroup> into dropdown and multi-select widgets. The <details> element renders <summary> as an expandable arrow. Even <video> knows how to deal with certain children: <source> elements don’t get rendered, but they do affect the video’s behavior. What magic!

Composition is what we normally do when we work with HTML. Since web components are merely HTML elements with a DOM reference — and not logical containers — we should rely on composition to build our components and any sub-components. If you think about the ul and and select you will notice that you declaratively compose these elements to get the final output and you have specific attributes to be used with the main component (e.g. [readonly]) or the sub-component (e.g. [selected]). This is true also for web components, and if you are building a custom list, consider to build the main component (<custom-list>) and the child one (<custom-li>). Using the [slot] element, you can define where children elements will be placed and also placeholder content that will be shown when no children are passed through.

Web components and accessibility

Another thing to consider is that “small” topic we call accessibility. Since we are creating completely new HTML elements, we need to consider the accessibility of our elements in order to provide a basic semantic role, any keyboard navigation as well as the user’s operating system preferences, like the reduce motion and high contrast settings.

I strongly suggest the following resources as reference for building accessible and inclusive components, how to define semantic markup, and how to implement a basic keyboard navigation.

Conclusion

Web components are an emerging technology in web development and, as such, there really aren’t any clearly defined best practices to guide us as far as building them for their intended or maximized use. When you find yourself working with them, perhaps the single thing you can take away from this post is to consider whether they are intended for a closed context or a standard context, then ask yourself WHI:

  • Who will use this component?
  • How much flexibility should that person have to customize it?
  • Is this component for everyone or for a specific audience?

The post Making Web Components for Different Contexts appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Designing for the web ought to mean making HTML and CSS

David Heinemeier Hansson has written an interesting post about the current state of web design and how designers ought to be able to still work on the code side of things:

We build using server-side rendering, Turbolinks, and Stimulus. All tools that are approachable and realistic for designers to adopt, since the major focus is just on HTML and CSS, with a few sprinkles of JavaScript for interactivity.

And it’s not like it’s some well kept secret! In fact, every single framework we’ve created at Basecamp that allows designers to work this way has been open sourced. The calamity of complexity that the current industry direction on JavaScript is unleashing upon designers is of human choice and design. It’s possible to make different choices and arrive at different designs.

I like this sentiment a whole lot — not every company needs to build their websites the same way. However, I don’t think that the approach that Basecamp has taken would scale to the size of a much larger organization. David continues:

Also not interested in retreating into the idea that you need a whole team of narrow specialists to make anything work. That “full-stack” is somehow a point of derision rather than self-sufficiency. That designers are so overburdened with conceptual demands on their creativity that they shouldn’t be bordered or encouraged to learn how to express those in the native materials of the web. Nope. No thanks!

Designing for the modern web in a way that pleases users with great, fast designs needn’t be this maze of impenetrable complexity. We’re making it that! It’s possible not to.

Again, I totally agree with David’s sentiment as I don’t think there’s anyone in the field who really wants to make the tools we use to build websites overly complicated; but in this instance, I tend to agree with what Nicolas recently had to say on this matter:

The interesting thing to note here is that the act of front-end development changes based on the size and scale of the organization. As with all arguments in front-end development, there is no “right” way! Our work has to adapt to the problems that we’re trying to solve. Is a large, complex React front-end useful for Basecamp? Maybe not. But for some organizations, like mine at Gusto, we have to specialize in certain areas because the product that we’re working on is so complicated.

I guess what I also might be rambling about is that I don’t think it’s engineers that are making front-end development complicated — perhaps it’s the expectations of our users.

Direct Link to ArticlePermalink

The post Designing for the web ought to mean making HTML and CSS appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Making Movies With amCharts

In this article, I want to show off the flexibility and real power of amCharts 4. We’re going to learn how to combine multiple charts that run together with animations that form a movie experience. Even if you’re only interested in creating a different kind of animation that has nothing to do with charts, you can still use this library, since it’s more than making charts. The core of amCharts is made to help with everything SVG: creation, layout, animation — basically a library that makes working with SVG fun!

Here’s the kind of thing I’m talking about. It’s actually a demonstration of seven different charts animated together. We’ll walk through this together, covering how it works and how to re-create it so you can have amCharts in your tool belt the next time you’re working with charts or complex animations.

See the Pen React Hook: setEffect example by amCharts team (@amcharts) on CodePen.

First, let’s outline the stages of the movie

There’s a lot going on in the movie. Let’s break it up into digestible parts that allow us to parse out what’s going on and re-create those parts under the hood.

Here’s the gist of what we’re working with:

  1. The initial animations and states
  2. The pie chart pops up
  3. The pie chart morphs to a country
  4. The plane flies to some other country
  5. The plane becomes big and flies away
  6. The column chart appears and bends to a radar column chart

The initial animations and states

The first thing we’re hit is a pie chart rising from the depths with a curved line wrapped around it. There’s nothing special about the pie chart at this point, but we’ll cover it in the next section.

But what about that curved line? Remember, we make charts, so this line is simply a XY chart with a single line on it. All the other details — grid, labels, tick marks, etc. — are hidden. So, what we’re really looking at is a stripped-down line chart!

Setting up the line and pie chart animations

amCharts calls the lines on this chart a line series. A line series has a variable called tensionX and, in this case, it’s been set to 0.75, making for a curvy line. We have to think of tension like we would a rope that is being held by two people and both ends: the tighter the rope is pulled, the greater the tension; conversely, the tension gets looser as the two ends let up. That 0.75 value is a taking a quarter of a unit away from the initial value (1), creating less tension.

// Creates the line on the chart var lineSeries = lineChart.series.push(new am4charts.LineSeries()); lineSeries.dataFields.dateX = "date"; lineSeries.dataFields.valueY = "value"; lineSeries.sequencedInterpolation = true; lineSeries.fillOpacity = 0.3; lineSeries.strokeOpacity = 0; lineSeries.tensionX = 0.75; Loosens the tension to create a curved line lineSeries.fill = am4core.color("#222a3f"); lineSeries.fillOpacity = 1;

Initially, all the values of the series are the same: a flat line. Then, we set valueY value of the line’s animation to 80, meaning it pops up to the eights row of the chart height — that will make plenty of room for the pie when it comes in.

// Defines the animation that reveals the curved line lineSeries.events.on("shown", function(){   setTimeout(showCurve, 2000) });  // Sets the animation properties and the valueY so the line bounces up to // 80 on the chart's y-axis function showCurve() {   lineSeries.interpolationEasing = am4core.ease.elasticOut;   lineSeries.dataItems.getIndex(3).setValue("valueY", 80, 2000);   setTimeout(hideCurve, 2000); }  // This is the initial state where the line starts at 30 on the y-axis // before it pops up to 80 function hideCurve() {   lineSeries.interpolationEasing = am4core.ease.elasticOut;   lineSeries.dataItems.getIndex(3).setValue("valueY", 30, 2000);   setTimeout(showCurve, 2000); }

Here is the line chart alone so we have a better visual for how that looks:

See the Pen deconstructing amCharts movie, stage 1 by amCharts team (@amcharts) on CodePen.

Next, the pie chart pops up from the bottom. Like a line series, amCharts includes a pie series and it has a dy property that we can set to hidden with a state of 400.

// Make the circle to show initially, meaning it will animate its properties from hidden state to default state circle.showOnInit = true;  // Set the circle's easing and the duration of the pop up animation circle.defaultState.transitionEasing = am4core.ease.elasticOut; circle.defaultState.transitionDuration = 2500;  // Make it appear from bottom by setting dy of hidden state to 300; circle.hiddenState.properties.dy = 300;

To illustrate this concept, here is a demo with that simple circle in place of a pie chart:

See the Pen deconstructing amCharts movie, initial animation by amCharts team (@amcharts) on CodePen.

A brief overview of amChart states

The idea of states in amCharts is this: you can have any number of custom states on any sprite. Then, instead of creating multiple animations with a slew of various different properties, state is changed from one to another and all the required properties that are set on the target state will animate from current values to the new state values.

Any numeric, percentage or color property of a sprite can be animated. By default, sprites have hidden and default states baked in. The hidden state is applied initially and followed by the revealed state, which is the default. There are other states, of course, like hover, active, disabled, among others, including custom states. Here is another demo showing a slice chart with innerRadius, radius and fill animating on hover:

See the Pen deconstructing amCharts movie, understanding states by amCharts team (@amcharts) on CodePen.

The pie chart pops up

Here is a demo of a basic pie chart. After some time, we’ll hide all the slices, except one, then show them all again.

See the Pen deconstructing amCharts movie, pie chart by amCharts team (@amcharts) on CodePen.

If you look at the code in the demo, you will see some of properties of the slices are customized via pieSeries.slices.template or pieSeries.labels.template. Most of the customization, of course, can be done using themes (amCharts 4 supports using multiple themes at the same time), but since we only need to change a few properties, we can use a template. We’re using a pie chart type and all of the slices of the pie series will be created using the provided template which passes any of the inherited properties we use from the template onto our pie chart.

// Call included themes for styling and animating am4core.useTheme(am4themes_amchartsdark); am4core.useTheme(am4themes_animated); // ... // Call the Pie Chart template var pieChart = mainContainer.createChild(am4charts.PieChart);

What if you want to set a custom color for the chart’s font? We can do this by adding a field in the data, like fontColor. That allows us to set custom colors there and then tell the label template that it should look at the field to inform the color property value.

// Define custom values that override one provided by the template pieChart.data = [{   "name": "[bold]No[/]",   "fontColor": am4core.color("#222a3f"),   "value": 220,   "tickDisabled":true }, {   "name": "Hm... I don't think so.",   "radius":20,   "value": 300,   "tickDisabled":false }, {   "name": "Yes, we are using amCharts",   "value": 100,   "labelDisabled": true,   "tickDisabled":true }];

Any property of a sprite can be customized like this. And even later, after the chart is initialized, we can change any property via the template, or if we need to access some individual object, we can get any value using something like series.slices.getIndex(3) to isolate it.

To summarize: there isn’t a single object on the chart that can’t be customized or accessed, changed, even after the chart is built. We’re working with a lot of flexibility!

The pie chart morphs into a country

I’ll be blunt: There is no way to morph a whole pie chart or some other complex object to the shape of a country. In amCharts 4, one polygon can morph into another one. And there are prebuilt methods that simply morph a polygon to a circle or to a rectangle. Here’s what we’ll do:

  • First, we hide all the slices of a pie chart, except one. This makes effectively transforms the remaining slice into a circle.
  • Then we animate the innerRadius property to 0, and the slice becomes a true circle.
  • There’s already a map chart at this moment, but it is hidden out of view. While it hides, we zoom into a selected country and morph it into a circle as well.
  • Next, we’ll show the country (which is now a circle) and hide the pie chart (which looks like the same circle at this time).
  • Finally, we morph the country back to its original shape.

Here is a simplified demo where we zoom in to the country, morph it to a circle, then morph it back to its default state:

See the Pen deconstructing amCharts movie, morphing by amCharts team (@amcharts) on CodePen.

Inspect that code. Note that all the methods we call, like zoomToMapObject, morphToCircle or morphBack, return an Animation object. An animation object dispatches events like animationstarted, animationprogress or animationended and we can attach listeners to them. This ensures that one animation is triggered only after another one is finished. And, if we change the duration of an animation, we won’t need to adjust timers accordingly, because events will handle it. In amCharts 4, Sprites, Components, DataItems, Animations, and other objects have an event dispatcher object which regulate any events. You can add listeners for these events and use them to make your applications super interactive.

The plane flies from one country to another

At one point, an airplane surfaces at London on a map chart and travels all the way to Silicon Valley.

It might look complex and scary, but it’s using a lot of the concepts we’ve already covered and the features come standard with the map chart included in amCharts:

  • MapImageSeries is created and sprites (circles and labels) are mapped to the actual longitude latitude coordinates of both cities.
// Create first image container var imageSeries = mapChart.series.push(new am4maps.MapImageSeries());  // London properties var city1 = imageSeries.mapImages.create(); // London's latitude/longitude city1.latitude = 51.5074; city1.longitude = 0.1278; // prevent from scaling when zoomed city1.nonScaling = true;  // New York City properties var city2 = imageSeries.mapImages.create(); // NY latitude/longitude city2.latitude = 40.7128; city2.longitude = -74.0060; // Prevent scaling when zoomed city2.nonScaling = true;
  • MapLineSeries, like the standard line series we saw earlier, creates a line between the cities based on the coordinates that are provided, going from one map image to another. By default, the line is drawn so that it follows the shortest distance between the objects. That happens to be a curved line in this case. We could make it a straight line if we’d like.
// Create the map line series var lineSeries = mapChart.series.push(new am4maps.MapLineSeries()); var mapLine = lineSeries.mapLines.create();  // Tell the line to connect the two cities (latitudes/longitudes an be used alternatively) mapLine.imagesToConnect = [city1, city2]  // Draw the line in dashes mapLine.line.strokeDasharray = "1,1"; mapLine.line.strokeOpacity = 0.2;
  • An object (plane) is added to the MapLine and it moves between the endpoints of the line by animating the plane’s position property from 0 to 1.
// Create the plane container var planeContainer = mapLine.lineObjects.create();  planeContainer.position = 0; // Set the SVG path of a plane for the sprite var plane = planeContainer.createChild(am4core.Sprite); planeContainer.nonScaling = false; planeContainer.scale = 0.015;  // SVG plane illustration plane.path = "M71,515.3l-33,72.5c-0.9,2.1,0.6,4.4,2.9,4.4l19.7,0c2.8,0,5.4-1,7.5-2.9l54.1-39.9c2.4-2.2,5.4-3.4,8.6-3.4 l103.9,0c1.8,0,3,1.8,2.3,3.5l-64.5,153.7c-0.7,1.6,0.5,3.3,2.2,3.3l40.5,0c2.9,0,5.7-1.3,7.5-3.6L338.4,554c3.9-5,9.9-8,16.2-8c24.2,0,85.5-0.1,109.1-0.2c21.4-0.1,41-6.3,59-17.8c4.2-2.6,7.9-6.1,11.2-9.8c2.6-2.9,3.8-5.7,3.7-8.5c0.1-2.8-1.1-5.5-3.7-8.5c-3.3-3.7-7-7.2-11.2-9.8c-18-11.5-37.6-17.7-59-17.8c-23.6-0.1-84.9-0.2-109.1-0.2c-6.4,0-12.3-2.9-16.2-8L222.6,316.6c-1.8-2.3-4.5-3.6-7.5-3.6l-40.5,0c-1.7,0-2.9,1.7-2.2,3.3L237,470c0.7,1.7-0.5,3.5-2.3,3.5l-103.9,0c-3.2,0-6.2-1.2-8.6-3.4l-54.1-39.9c-2.1-1.9-4.7-2.9-7.5-2.9l-19.7,0c-2.3,0-3.8,2.4-2.9,4.4l33,72.5C72.6,507.7,72.6,511.8,71,515.3z"; plane.fill = am4core.color("#eeeab5");  plane.horizontalCenter = "middle"; plane.verticalCenter = "middle";

Here is a demo of a plane flying from London to New York:

See the Pen deconstructing amCharts movie, map part by amCharts team (@amcharts) on CodePen.

Notice that the plane becomes bigger when it hits the line’s halfway point? This is done with three additional lines of code we can stick at the end.

// Make the plane to be bigger in the middle of the line planeContainer.adapter.add("scale", function(scale, target) {   return (0.07 - 0.10 * (Math.abs(0.5 - target.position))) / mapChart.zoomLevel; })

We’re using a method that called an adapter, which is another super-powerful feature of amCharts 4. In this case, the adapter modifies the scale property (0.07 to 0.10), based on planes position (0.5).

The plane becomes big and flies away

When our plane reaches the target city (Silicon Valley in the full movie), we scale and rotate it to become horizontal and big.

Animation of a simple plane illustration popping up over a point on a map at Silicon Valley. The plane starts small and then zooms in to a larger size that makes it appear up close.

At the same moment, we create another chart (the SlicedChart type) and add a PictorialSeries to it. The series share’s the same path as the plane, which creates a mask for the slices. We can use any SVG path here.

When the slices are shown, we want the plane to fly away:

Animation of the zoomed plane illustration from the previous image taking off and flying across the screen from left to right, leaving a chart behind it as it exits the screen.

This happens by animating the chart object’s dx property.

flyAway()  function flyAway(){   var animation = pictorialChart.animate({property:"dx", to:2000}, 1500, am4core.ease.quadIn).delay(2000);   animation.events.on("animationended", flyIn); }  function flyIn(){   var animation = pictorialChart.animate({property:"dx", from:-2000, to:0}, 1500, am4core.ease.quadOut);   animation.events.on("animationended", flyAway); }

Here is a demo of a Sliced chart:

See the Pen deconstructing amCharts movie, pictorial series by amCharts team (@amcharts) on CodePen.

The trail of a plane is again made with a line series, similar to the one we had in the beginning. This time, it’s a combination of two separate series: one with positive and another with negative values. When a series has the sequencedInterpolation property set to true, the animation happens with some delay for each data value and we get an effect like this:

See the Pen deconstructing amCharts movie, plane trail by amCharts team (@amcharts) on CodePen.

var series1 = chart.series.push(new am4charts.LineSeries()) series1.dataFields.dateX = "date"; series1.dataFields.valueY = "value1"; series1.sequencedInterpolation = true; series1.fillOpacity = 1; series1.tensionX = 0.8; series1.stroke = am4core.color("#222a3f") series1.fill = am4core.color("#222a3f")

Then there’s the silhouette of a cityscape that scrolls horizontally as the plane passes by:

See the Pen deconstructing amCharts movie, city passing by by amCharts team (@amcharts) on CodePen.

This uses the same chart as the plane trail. We basically add another value axis to the chart and create a column series:

// Add a new axis to the chart var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); // ... shortened for brevity  // Configure the column series var series = chart.series.push(new am4charts.ColumnSeries()) series.dataFields.dateX = "date"; series.dataFields.valueY = "value"; series.sequencedInterpolation = true; series.fillOpacity = 1; series.fill = am4core.color("#222a3f"); series.stroke = am4core.color("#222a3f")  // Establish the columns at full width series.columns.template.width = am4core.percent(100);

Then we update the background to gradient:

chart.background.fillOpacity = 0.2;   var gradient = new am4core.LinearGradient();   gradient.addColor(am4core.color("#222a3f"));   gradient.addColor(am4core.color("#0975da"));   gradient.rotation = -90;   chart.background.fill = gradient;

And finally, we zoom in on the chart to half of the total range so that we can slowly change the zoom level later to create the moving city effect.

function startZoomAnimation(){   // Animate the start and end values slowly to make it look like the city is moving   var animation = dateAxis.animate([{property:"start", from:0, to:0.5}, {property:"end", from:0.5, to:1}], 15000, am4core.ease.linear);   animation.events.on("animationended", startZoomAnimation); }

Here is all the code, without the trail part for brevity:

am4core.useTheme(am4themes_amchartsdark); am4core.useTheme(am4themes_animated);  // Main container var mainContainer = am4core.create("introchart", am4core.Container); mainContainer.width = am4core.percent(100); mainContainer.height = am4core.percent(100);  var chart = mainContainer.createChild(am4charts.XYChart); chart.padding(0, 0, 0, 0) chart.zIndex = 20;  var data = []; var date = new Date(2000, 0, 1, 0, 0, 0, 0);  for (var i = 0; i < 40; i++) {   var newDate = new Date(date.getTime());   newDate.setDate(i + 1);    var value = Math.abs(Math.round(((Math.random() * 100 - i + 10) / 10)) * 10)    data.push({ date: newDate, value: value }); }  chart.data = data; chart.zoomOutButton.disabled = true; chart.seriesContainer.zIndex = -1;  chart.background.fillOpacity = 0.2; var gradient = new am4core.LinearGradient(); gradient.addColor(am4core.color("#222a3f")); gradient.addColor(am4core.color("#0975da")); gradient.rotation = -90; chart.background.fill = gradient;  var dateAxis = chart.xAxes.push(new am4charts.DateAxis()); dateAxis.renderer.grid.template.location = 0; dateAxis.renderer.ticks.template.disabled = true; dateAxis.renderer.axisFills.template.disabled = true;  dateAxis.renderer.labels.template.disabled = true; dateAxis.rangeChangeEasing = am4core.ease.sinIn; dateAxis.renderer.inside = true; dateAxis.startLocation = 0.5; dateAxis.endLocation = 0.5; dateAxis.renderer.baseGrid.disabled = true; dateAxis.tooltip.disabled = true; dateAxis.renderer.line.disabled = true; dateAxis.renderer.grid.template.strokeOpacity = 0.07;  var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.tooltip.disabled = true; valueAxis.renderer.ticks.template.disabled = true; valueAxis.renderer.axisFills.template.disabled = true; valueAxis.renderer.labels.template.disabled = true; valueAxis.renderer.inside = true; valueAxis.min = 0; valueAxis.max = 100; valueAxis.strictMinMax = true; valueAxis.tooltip.disabled = true; valueAxis.renderer.line.disabled = true; valueAxis.renderer.baseGrid.disabled = true; valueAxis.renderer.grid.template.strokeOpacity = 0.07;  var series = chart.series.push(new am4charts.ColumnSeries()) series.dataFields.dateX = "date"; series.dataFields.valueY = "value"; series.sequencedInterpolation = true; series.fillOpacity = 1; series.fill = am4core.color("#222a3f"); series.stroke = am4core.color("#222a3f")  series.columns.template.width = am4core.percent(100);  chart.events.on("ready", startZoomAnimation);  function startZoomAnimation(){   // Animate the start and end values slowly to make it look like city is moving   var animation = dateAxis.animate([{property:"start", from:0, to:0.5}, {property:"end", from:0.5, to:1}], 15000, am4core.ease.linear);   animation.events.on("animationended", startZoomAnimation); }

The column chart appears and bends to a radar column chart

Can you guess what happens in the final scene? The initial chart (which looks like a regular column chart) is actually what’s called a radar chart with a very small arc between the chart’s startAngle (269.9°) and endAngle (270.1°) properties.

var radarChart = mainContainer.createChild(am4charts.RadarChart); // ... Chart properties go here  radarChart.startAngle = 269.9; radarChart.endAngle = 270.1;

The total arc angle is only 0.2° degrees and that’s why the radius of a chart becomes very big and tough tell it apart from a regular XY chart. And all we do to bend the chart is animate the start and end angles. I told you… we can literally animate everything, including angles!

radarChart.events.on("ready", bend);  function bend() {   var animation = radarChart.animate([{ property: "startAngle", to: 90 }, { property: "endAngle", to: 450 }], 3500, am4core.ease.cubicIn).delay(1000);     animation.events.on("animationended", unbend); }  function unbend() {   var animation = radarChart.animate([{ property: "startAngle", to: 269.9 }, { property: "endAngle", to: 270.1 }], 3500, am4core.ease.cubicIn).delay(500);   animation.events.on("animationended", bend); }

Here’s that bending animation in all its glory:

See the Pen deconstructing amCharts movie, bending the chart by amCharts team (@amcharts) on CodePen.

OK, we are done!

Oh, but there is one last, important thing I would like to mention: Containers. All charts and other non-chart objects in this movie are contained in a single div element. Initially, we created mainConatainer and arranged all the objects inside it. Containers support horizontal, vertical, grid and absolute layouts. Fixed or absolute positions can be used for a container’s children, and containers can be nested inside other containers. They can be aligned horizontally or vertically, set to a fixed or absolute width or height… and so on. And did I mentioned, that amCharts has built-in date, number and text formatters? Seriously, I will stop here.

As a part of the amCharts team, I often hear comments like, “but you can do all of this with d3.” Yes, you are probably right. But there are still real benefits we’re working with here — fewer lines of code, time spent writing it, and a relatively simple startup. All the animation is made up of 1,000 lines of code, so this is also a pretty lightweight resource.

But, I’m really interested in what you think. Have you tried amCharts before and have examples to show off? How about questions about getting started? Or maybe you’re not sold on the concept altogether and want to chat the pros and cons? Let’s hear it in the comments!

The post Making Movies With amCharts appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Making SVG icon libraries for React apps

Nicolas Gallagher:

At Twitter I used the approach described here to publish the company’s SVG icon library in several different formats: optimized SVGs, plain JavaScript modules, React DOM components, and React Native components.

There is no One True Way© to make an SVG icon system. The only thing that SVG icon systems have in common is that, somehow, some way, SVG is used to show that icon. I gotta find some time to write up a post that goes into all the possibilities there.

One thing different systems tend to share is some kind of build process to turn a folder full of SVG files into a more programmatically digestible format. For example, gulp-svg-sprite takes your folder of SVGs and creates a SVG sprite (chunk of <symbol>s) to use in that type of SVG icon system. Grunticon processes your folder of SVGs into a CSS file, and is capable of enhancing them into inline SVG. Gallagher’s script creates React components out of them, and like he said, that’s great for delivery to different targets as well as performance optimization, like code splitting.

This speaks to the versatility of SVG. It’s just markup, so it’s easy to work with.

Direct Link to ArticlePermalink

The post Making SVG icon libraries for React apps appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]