Rethinking Code Comments

Justin Duke asks if treating code comments like footnotes could help us understand the code in a file better. In his mockup, all the comments are hidden by default and require a click to reveal:

What a neat idea! Justin’s design reminds me of the way that Instapaper treated inline footnotes.

Instapaper (circa 2012)

I guess the reason I like this idea so much is that a lot of comments don’t need to be read constantly, — they’re sort of a reminder that, “Hey, this needs work in the future” or “Yikes, this is weird and I’m sorry.” Keeping these comments out of the code makes it much easier to scan the whole file, too.

I do wonder if there could be a toggle that shows every comment, just in case you need to read all the comments in sequence rather than clicking to toggle each one.

Anyway, all this talk about comments reminds me of an absolutely fantastic talk by Sarah Drasner at JSConf this year where she discussed why comments are so dang hard to get right:

Direct Link to ArticlePermalink

The post Rethinking Code Comments appeared first on CSS-Tricks.

Introducing Alpine.js: A Tiny JavaScript Framework

Introducing Alpine.js: A Tiny JavaScript Framework

Introducing Alpine.js: A Tiny JavaScript Framework

Phil Smith

Like most developers, I have a bad tendency to over-complicate my workflow, especially if there’s some new hotness on the horizon. Why use CSS when you can use CSS-in-JS? Why use Grunt when you can use Gulp? Why use Gulp when you can use Webpack? Why use a traditional CMS when you can go headless? Every so often though, the new-hotness makes life simpler.

Recently, the rise of utility based tools like Tailwind CSS have done this for CSS, and now Alpine.js promises something similar for JavaScript.

In this article, we’re going to take a closer look at Alpine.js and how it can replace JQuery or larger JavaScript libraries to build interactive websites. If you regularly build sites that require a sprinkling on Javascript to alter the UI based on some user interaction, then this article is for you.

Throughout the article, I refer to Vue.js, but don’t worry if you have no experience of Vue — that is not required. In fact, part of what makes Alpine.js great is that you barely need to know any JavaScript at all.

Now, let’s get started.

What Is Alpine.js?

According to project author Caleb Porzio:

“Alpine.js offers you the reactive and declarative nature of big frameworks like Vue or React at a much lower cost. You get to keep your DOM, and sprinkle in behavior as you see fit.”

Let’s unpack that a bit.

Let’s consider a basic UI pattern like Tabs. Our ultimate goal is that when a user clicks on a tab, the tab contents displays. If we come from a PHP background, we could easily achieve this server side. But the page refresh on every tab click isn’t very ‘reactive’.

To create a better experience over the years, developers have reached for jQuery and/or Bootstrap. In that situation, we create an event listener on the tab, and when a user clicks, the event fires and we tell the browser what to do.

See the Pen Showing / hiding with jQuery by Phil on CodePen.

See the Pen Showing / hiding with jQuery by Phil on CodePen.

That works. But this style of coding where we tell the browser exactly what to do (imperative coding) quickly gets us in a mess. Imagine if we wanted to disable the button after it has been clicked, or wanted to change the background color of the page. We’d quickly get into some serious spaghetti code.

Developers have solved this issue by reaching for frameworks like Vue, Angular and React. These frameworks allow us to write cleaner code by utilizing the virtual DOM: a kind of mirror of the UI stored in the browser memory. The result is that when you ‘hide’ a DOM element (like a tab) in one of these frameworks; it doesn’t add a display:none; style attribute, but instead it literally disappears from the ‘real’ DOM.

This allows us to write more declarative code that is cleaner and easier to read. But this is at a cost. Typically, the bundle size of these frameworks is large and for those coming from a jQuery background, the learning curve feels incredibly steep. Especially when all you want to do is toggle tabs! And that is where Alpine.js steps in.

Like Vue and React, Alpine.js allows us to write declarative code but it uses the “real” DOM; amending the contents and attributes of the same nodes that you and I might edit when we crack open a text editor or dev-tools. As a result, you can lose the filesize, wizardry and cognitive-load of larger framework but retain the declarative programming methodology. And you get this with no bundler, no build process and no script tag. Just load 6kb of Alpine.js and you’re away!

Alpine.js JQuery Vue.js React + React DOM
Coding style Declarative Imperative Declarative Declarative
Requires bundler No No No Yes
Filesize (GZipped, minified) 6.4kb 30kb 32kb 5kb + 36kb
Dev-Tools No No Yes Yes

When Should I Reach For Alpine?

For me, Alpine’s strength is in the ease of DOM manipulation. Think of those things you used out of the box with Bootstrap, Alpine.js is great for them. Examples would be:

  • Showing and hiding DOM nodes under certain conditions,
  • Binding user input,
  • Listening for events and altering the UI accordingly,
  • Appending classes.

You can also use Alpine.js for templating if your data is available in JSON, but let’s save that for another day.

When Should I Look Elsewhere?

If you’re fetching data, or need to carry out additional functions like validation or storing data, you should probably look elsewhere. Larger frameworks also come with dev-tools which can be invaluable when building larger UIs.

From jQuery To Vue To Alpine

Two years ago, Sarah Drasner posted an article on Smashing Magazine, “Replacing jQuery With Vue.js: No Build Step Necessary,” about how Vue could replace jQuery for many projects. That article started me on a journey which led me to use Vue almost every time I build a user interface. Today, we are going to recreate some of her examples with Alpine, which should illustrate its advantages over both jQuery and Vue in certain use cases.

Alpine’s syntax is almost entirely lifted from Vue.js. In total, there are 13 directives. We’ll cover most of them in the following examples.

Getting Started

Like Vue and jQuery, no build process is required. Unlike Vue, Alpine it initializes itself, so there’s no need to create a new instance. Just load Alpine and you’re good to go.

<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v1.9.4/dist/alpine.js" defer></script>

The scope of any given component is declared using the x-data directive. This kicks things off and sets some default values if required:

<div x-data="{ foo: 'bar' }">...</div>

Capturing User Inputs

x-model allow us to keep any input element in sync with the values set using x-data. In the following example, we set the name value to an empty string (within the form tag). Using x-model, we bind this value to the input field. By using x-text, we inject the value into the innerText of the paragraph element.

See the Pen Capturing user input with Alpine.js by Phil on CodePen.

See the Pen Capturing user input with Alpine.js by Phil on CodePen.

This highlights the key differences with Alpine.js and both jQuery and Vue.js.

Updating the paragraph tag in jQuery would require us to listen for specific events (keyup?), explicitly identify the node we wish to update and the changes we wish to make. Alpine’s syntax on the other hand, just specifies what should happen. This is what is meant by declarative programming.

Updating the paragraph in Vue while simple, would require a new script tag:

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

While this might not seem like the end of the world, it highlights the first major gain with Alpine. There is no context-switching. Everything is done right there in the HTML — no need for any additional JavaScript.

Click Events, Boolean Attributes And Toggling Classes

Like with Vue, : serves as a shorthand for x-bind (which binds attributes) and @ is shorthand for x-on (which indicates that Alpine should listen for events).

In the following example, we instantiate a new component using x-data, and set the default value of show to be false. When the button is clicked, we toggle the value of show. When this value is true, we instruct Alpine to append the aria-expanded attribute.

x-bind works differently for classes: we pass in object where the key is the class-name (active in our case) and the value is a boolean expression (show).

See the Pen Click Events, Boolean Attributes and Toggling Classes with Alpine.js by Phil on CodePen.

See the Pen Click Events, Boolean Attributes and Toggling Classes with Alpine.js by Phil on CodePen.

Hiding And Showing

The syntax showing and hiding is almost identical to Vue.

See the Pen Showing / hiding with Alpine.js by Phil on CodePen.

See the Pen Showing / hiding with Alpine.js by Phil on CodePen.

This will set a given DOM node to display:none. If you need to remove a DOM element completely, x-if can be used. However, because Alpine.js doesn’t use the Virtual DOM, x-if can only be used on a <template></template> (tag that wraps the element you wish to hide).

Magic Properties

In addition to the above directives, three Magic Properties provide some additional functionality. All of these will be familiar to anyone working in Vue.js.

  • $el fetches the root component (the thing with the x-data attribute);
  • $refs allows you to grab a DOM element;
  • $nextTick ensures expressions are only executed once Alpine has done its thing;
  • $event can be used to capture a nature browser event.

See the Pen Magic Properties by Phil on CodePen.

See the Pen Magic Properties by Phil on CodePen.

Let’s Build Something Useful

It’s time to build something for the real world. In the interests of brevity I’m going to use Bootstrap for styles, but use Alpine.js for all the JavaScript. The page we’re building is a simple landing page with a contact form displayed inside a modal that submits to some form handler and displays a nice success message. Just the sort of thing a client might ask for and expect pronto!

Initial view of the demo app
Initial view (Large preview)
View of the demo app with modal open
Modal open (Large preview)
View of the demo app with success message displaying
Success message (Large preview)

Note: You can view the original markup here.

To make this work, we could add jQuery and Bootstrap.js, but that is quite a bit of overhead for not a lot of functionality. We could probably write it in Vanilla JS, but who wants to do that? Let’s make it work with Alpine.js instead.

First, let’s set a scope and some initial values:

<body class="text-center text-white bg-dark h-100 d-flex flex-column" x-data="{ showModal: false, name: '', email: '', success: false }">

Now, let’s make our button set the showModal value to true:

<button class="btn btn-lg btn-secondary" @click="showModal = true" >Get in touch</button>
 

When showModal is true, we need to display the modal and add some classes:

<div class="modal  fade text-dark" :class="{ 'show d-block': showModal }" x-show="showModal" role="dialog">
 

Let’s bind the input values to Alpine:

<input type="text" class="form-control" name="name" x-model="name" >
<input type="email" class="form-control" name="email" x-model="email" >
 

And disable the ‘Submit’ button, until those values are set:

<button type="button" class="btn btn-primary" :disabled="!name || !email">Submit</button>

Finally, let’s send data to some kind of asynchronous function, and hide the modal when we’re done:

<button type="button" class="btn btn-primary" :disabled="!name || !email" @click="submitForm({name: name, email: email}).then(() => {showModal = false; success= true;})">Submit</button>
 

And that’s about it!

See the Pen Something useful built with Alpine.js by Phil on CodePen.

See the Pen Something useful built with Alpine.js by Phil on CodePen.

Just Enough JavaScript

When building websites, I’m increasingly trying to ask myself what would be “just enough JavaScript”? When building a sophisticated web application, that might well be React. But when building a marketing site, or something similar, Alpine.js feels like enough. (And even if it’s not, given the similar syntax, switching to Vue.js takes no time at all).

It’s incredibly easy to use (especially if you’ve never used VueJS). It’s tiny (< 6kb gzipped). And it means no more context switching between HTML and JavaScript files.

There are more advanced features that aren’t included in this article and Caleb is constantly adding new features. If you want to find out more, take a look at the official docs on Github.

Smashing Editorial (ra, il)

Collective #585






Collective Item Image

YourStack

YourStack is a place to share and discover your favorite products. Now in public beta.

Check it out






Collective Item Image

The Year of Greta

An interactive timeline of how Greta Thunberg rose from a solo campaigner to the leader of a global movement in 2019.

Check it out



Collective Item Image

Lingua Franca

Lingua Franca is a design language for human-centered AI – a set of guidelines that apply to any AI-driven product, tool, service, or experience, to bring coherence and fluidity to otherwise complex and messy technologies.

Check it out


Collective Item Image

PPL MVR

An animated recreation of a PPL MVR band poster. By Kristopher Van Sant.

Check it out




Collective Item Image

HanaGL

Such an awesome WebGL demo! Put the finger in the nose and see what happens 🙂

Check it out






Collective #585 was written by Pedro Botelho and published on Codrops.

How to Turn a Procreate Drawing into a Web Animation

I recently started drawing on my iPad using the Procreate app with Apple Pencil. I’m enjoying the flexibility of drawing this way. What usually keeps me from painting at home are basic things, like setup, cleaning brushes, proper ventilation, and other factors not really tied to the painting itself. Procreate does a pretty nice job of emulating painting and drawing processes, but adding digital features like undo/redo, layers, and layer effects.

Here’s a Procreate painting I made that I wound up exporting and animating on the web.

See the Pen
Zebra Page- web animation from a Procreate drawing
by Sarah Drasner (@sdras)
on CodePen.

You can do this too! There are two basic animation effects we’ll cover here: the parallax effect that takes place on hover (with the ability to turn it off for those with vestibular disorders), and the small drawing effect when the page loads.

Parallax with drawing layers

I mentioned that part of the reason I enjoy drawing on the iPad is the ability to work in layers. When creating layers, I take care to keep certain “themes” on the same layer, for instance, the zebra stripes are on one layer and the dots are on own layer under beneath the stripes.

I’ll extend the drawing beyond the boundaries of where the line from the layer above ends, mainly because you’ll be able to peek around it a bit as we move the drawing around in the parallax effect. If the lines are sharp at any point, this will look unnatural.

Once I'm done creating my layers, I can export things as a Photoshop (PSD) file, thanks to Procreate's exporting options.

The same drawing opened in Photoshop.

Then I’ll join together a few, so that I’m only working with about 8 layers at most. I use a photoshop plugin called tinyPNG to export each layer individually. I’ve heard there are better compression tools, but I’ve been pretty happy with this one.

Next, I'll go into my code editor and create a div to house all the various images that are contained in the layers. I give that div relative positioning while all of the images inside it get absolute positioning. This places the images one on top the other.

<div id="zebra-ill" role="presentation">
  <img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport6.png' />
  <img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport5.png' />
 …
</div>
#zebra-ill {
  position: relative;
  min-height: 650px;
  max-width: 500px;
}

.zebraimg {
  position: absolute;
  top: 0;
  left: 0;
  perspective: 600px;
  transform-style: preserve-3d;
  transform: translateZ(0);
  width: 100%;
  }

The 100% width on the image will confines all of the images to the size of the parent div. I do this so that I’m controlling them all at once with the same restrictions, which works well for responsive conditions. The max-width and min-height on the parent allow me to confine the way that the div shrinks and grows, especially as when it gets dropped into a CSS Grid layout. It will need to be flexible, but have some constraints as well and CSS Grid is great for that.

Next, I add a mousemove event listener on the parent div with JavaScript. That lets me capture some information about the coordinates of the mouse using e.clientX and e.clientY.

const zebraIll = document.querySelector('#zebra-ill')

// Hover
zebraIll.addEventListener('mousemove', e => {
  let x = e.clientX;
  let y = e.clientY;
})

Then, I’ll go through each of the drawings and use those coordinates to move the images around. I'll even apply transform styles connected to those coordinates.

const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05

//hover
zebraIll.addEventListener('mousemove', e => {
  let x = e.clientX;
  let y = e.clientY;
  
  zebraIllImg.forEach((el, index) => {
    el.style.transform = 
      `rotateX(${x}deg) rotateY(${y}deg)`
  })
})

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

Woah, slow down there partner! That’s way too much movement, we want something a little more subtle. So I’ll need to slow it way down by multiplying it by a low rate, like 0.05. I also want to change it a just bit per layer, so I'll use the layers index to speed up or slow down the movement.

const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05

// Hover
zebraIll.addEventListener('mousemove', e => {
  let x = e.clientX;
  let y = e.clientY;
  
  zebraIllImg.forEach((el, index) => {
    let speed = index += 1
    let xPos = speed + rate * x
    let yPos = speed + rate * y
    
    el.style.transform = 
      `rotateX(${xPos - 20}deg) rotateY(${yPos - 20}deg) translateZ(${index * 10}px)`
  })
})

Finally, I can create a checkbox that asks the user if want to turn off this effect.

<p>
  <input type="checkbox" name="motiona11y" id="motiona11y" />
  <label for="motiona11y">If you have a vestibular disorder, check this to turn off some of the effects</label>
</p>
const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05
const motioncheck = document.getElementById('motiona11y')
let isChecked = false

// Check to see if someone checked the vestibular disorder part
motioncheck.addEventListener('change', e => {
  isChecked = e.target.checked;
})

// Hover
zebraIll.addEventListener('mousemove', e => {
  if (isChecked) return
  let x = e.clientX;
  let y = e.clientY;
  
  // ...
})

Now the user has the ability to look at the layered dimensionality of the drawing on hover, but can also turn the effect off if it is bothersome.

Drawing Effect

The ability to make something look like it’s been drawn on to the page has been around for a while and there are a lot of articles on how it’s done. I cover it as well in a course I made for Frontend Masters.

The premise goes like this:

  • Take an SVG path and make it dashed with dashoffset.
  • Make the dash the entire length of the shape.
  • Animate the dashoffset (the space between dashes).

What you get in the end is a kind of “drawn-on” effect.

But in this particular drawing you might have noticed that the parts I animated look like they were hand-drawn, which is a little more unique. You see, though that effect will work nicely for more mechanical drawings, the web doesn’t quite yet support the use of tapered lines (lines that vary in thickness, as is typical of a more hand-drawn feel).

For this approach, I brought the file into Illustrator, traced the lines from that part of my drawing, and made those lines tapered by going into the Stroke panel, where I selected "More options" and clicked the tapered option from the dropdown.

Screenshot of the Illustrator Stroke menu.

I duplicated those lines, and created fatter, uniform paths underneath. I then took those fat lines and animate them onto the page. Now my drawing shows through the shape:

Here's what I did:

  • I traced with Pen tool and used tapered brush.
  • I duplicated the layer and changed the lines to be uniform and thicker.
  • I took the first layer and created a compound path.
  • I simplified path points.
  • I created clipping mask.

From there, I can animate everything with drawSVG and GreenSock. Though you don’t need to, you could use CSS for this kind of animation. There’s a ton of path points so in this case, so it makes sense to use something more powerful. I wrote another post that goes into depth on how to start off creating these kinds of animations. I would recommend you start there if you’re fresh to it.

To use drawSVG, we need to do a few things:

  • Load the plugin script.
  • Register the plugin at the top of the JavaScript file.
  • Make sure that paths are being used, and that there are strokes on those paths.
  • Make sure that those paths are targeted rather than the groups that house them. The parent elements could be targeted instead.

Here’s a very basic example of drawSVG (courtesy of GreenSock):

See the Pen
DrawSVGPlugin Values
by GreenSock (@GreenSock)
on CodePen.

So, in the graphics editor, there is a clipping mask with more artful lines, that expose fat uniform lines underneath. From here, we’ll grab a hold of those thicker paths and use the drawSVG plugin to animate them onto the page.

//register the plugin
gsap.registerPlugin(DrawSVGPlugin);

const drawLines = () => {
  gsap.set('.cls-15, #yellowunderline, .cls-13', {
    visibility: 'visible'
  })
  
  const timeline = gsap.timeline({ 
    defaults: {
      delay: 1,
      ease: 'circ',
      duration: 2
    }		  
  })
  .add('start')
  .fromTo('.cls-15 path', {
    drawSVG: '0%'
  }, {
    drawSVG: '100%',
    immediateRender: true
  }, 'start')
  .fromTo('#yellowunderline path', {
    drawSVG: '50% 50%'
  }, {
    drawSVG: '100%',
    immediateRender: true
  }, 'start+=1')
  .fromTo('.cls-13', {
    drawSVG: '50% 50%'
  }, {
    drawSVG: '100%',
    immediateRender: true
  }, 'start+=1')
}

window.onload = () => {
  drawLines()
};

And there we have it! An initial illustration for our site that’s created from a layered drawing in the Procreate iPad app. I hope this gets you going making your web projects unique with wonderful hand-drawn illustrations. If you make anything cool, let us know in the comments below!

The post How to Turn a Procreate Drawing into a Web Animation appeared first on CSS-Tricks.

Netlify High-Fives

We've got Netlify as a sponsor around here again this year, which is just fantastic. Big fan. Our own Sarah Drasner is Head of DX (Developer Experience) over there, if you hadn't heard. And if you haven't heard of Netlify, well, you're in for a treat. It's a web host, but for your jamstack sites, which means it's static hosting, encouraging you to pre-build as much of your site as you can, then use JavaScript and APIs to do whatever else you need to. Heck, they'll help you build serverless functions and auth.

Here's a couple of Netlify-related things swirling around in my life.

  • I added open authoring to our conference site. So now, anybody can go to the admin area, auth with GitHub, and submit a conference. No coding required. Dream come true, if you ask me. The same thing is live on the serverless site.
  • I enjoyed Bryan Robinson's take on jamstack in 2020. It's about the connection between services and the power that brings.
  • It's interesting how even not-particularly jamstack-y software like WordPress can totally live a jamstack life, when you combine it with something like Gatsby.
  • I have a new microsite idea cooking. I really wanna build a site that showcases all the things you can (and probably should) be doing with your build process. It will explain them and provide resources, but the whole site will dogfood itself and do all those things. Stuff like:
    1. Process all it's code, keeping compiled code out of the repo
    2. Build a sitemap
    3. Optimize all the images
    4. Check for broken links
    5. Run accessibility tests
    6. Check the performance budget
    7. Run unit test
    8. Run end-to-end tests
    9. Run visual regression tests
    10. Trigger notifications

Wouldn't that last one be cool?! We'd do it all with build plugins, at least that's how it works in my mind. If you have a strong desire to contribute, lemme know — maybe we can make it a community effort.

The post Netlify High-Fives appeared first on CSS-Tricks.

Understanding Async Await

When writing code for the web, eventually you'll need to do some process that might take a few moments to complete. JavaScript can't really multitask, so we'll need a way to handle those long-running processes.

Async/Await is a way to handle this type of time-based sequencing. It’s especially great for when you need to make some sort of network request and then work with the resulting data. Let's dig in!

Promise? Promise.

Async/Await is a type of Promise. Promises in JavaScript are objects that can have multiple states (kind of like the real-life ones ☺️). Promises do this because sometimes what we ask for isn't available immediately, and we'll need to be able to detect what state it is in.

Consider someone asks you to promise to do something for them, like help them move. There is the initial state, where they have asked. But you haven't fulfilled your promise to them until you show up and help them move. If you cancel your plans, you rejected the promise.

Similarly, the three possible states for a promise in JavaScript are:

  • pending: when you first call a promise and it’s unknown what it will return.
  • fulfilled: meaning that the operation completed successfully
  • rejected: the operation failed

Here’s an example of a promise in these states:

Here is the fulfilled state. We store a promise called getSomeTacos, passing in the resolve and reject parameters. We tell the promise it is resolved, and that allows us to then console log two more times.

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  resolve();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Order some tacos
> Here are your tacos

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

If we choose the rejected state, we'll do the same function but reject it this time. Now what will be printed to the console is the Initial State and the catch error:

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  reject();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Nope! No tacos for you.

And when we select the pending state, we'll simply console.log what we stored, getSomeTacos. This will print out a pending state because that's the state the promise is in when we logged it!

console.log(getSomeTacos)
> Initial state: Excuse me can I have some &#x1f32e;s
> Promise {<pending>}
> Order some &#x1f32e;s
> Here are your &#x1f32e;s

What then?

But here’s a part that was confusing to me at first. To get a value out of a promise, you have to use .then() or something that returns the resolution of your promise. This makes sense if you think about it, because you need to capture what it will eventually be — rather than what it initially is — because it will be in that pending state initially. That's why we saw it print out Promise {<pending>} when we logged the promise above. Nothing had resolved yet at that point in the execution.

Async/Await is really syntactic sugar on top of those promises you just saw. Here's a small example of how I might use it along with a promise to schedule multiple executions.

async function tacos() {
  return await Promise.resolve("Now and then I get to eat delicious tacos!")
};

tacos().then(console.log)

Or a more in-depth example:

// this is the function we want to schedule. it's a promise.
const addOne = (x) => {
  return new Promise(resolve => {
    setTimeout(() => { 
      console.log(`I added one! Now it's ${x + 1}.`)
      resolve()
    }, 2000);
  })
}

// we will immediately log the first one, 
// then the addOne promise will run, taking 2 seconds
// then the final console.log will fire
async function addAsync() {
  console.log('I have 10')
  await addOne(10)
  console.log(`Now I'm done!`)
}

addAsync()
> I have 10
> I added one! Now it's 11.
> Now I'm done!

See the Pen
Async Example 1
by Sarah Drasner (@sdras)
on CodePen.

One thing (a)waits for another

One common use of Async/Await is to use it to chain multiple asynchronous calls. Here, we'll fetch some JSON that we'll use to pass into our next fetch call to figure out what type of thing we want to fetch from the second API. In our case, we want to access some programming jokes, but we first need to find out from a different API what type of quote we want.

The first JSON file looks like this- we want the type of quote to be random:

{
  "type": "random"
}

The second API will return something that looks like this, given that random query parameter we just got:

{
  "_id":"5a933f6f8e7b510004cba4c2",
  "en":"For all its power, the computer is a harsh taskmaster. Its programs must be correct, and what we wish to say must be said accurately in every detail.",
  "author":"Alan Perlis",
  "id":"5a933f6f8e7b510004cba4c2"
}

We call the async function then let it wait to go retrieve the first .json file before it fetches data from the API. Once that happens, we can do something with that response, like add it to our page.

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteTypeResponse = await fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
  let quoteType = await quoteTypeResponse.json()

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quoteResponse = await fetch("https://programming-quotes-api.herokuapp.com/quotes/" + quoteType.type)
  let quote = await quoteResponse.json()

  // finish up
  console.log('done')
}

We can even simplify this using template literals and arrow functions:

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteType = await fetch(`quotes.json`).then(res => res.json())

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

  // finish up
  console.log('done')
}

getQuote()

Here is an animated explanation of this process.

See the Pen
Animated Description of Async Await
by Sarah Drasner (@sdras)
on CodePen.

Try, Catch, Finally

Eventually we’ll want to add error states to this process. We have handy try, catch, and finally blocks for this.

try {
  // I’ll try to execute some code for you
}
catch(error) {
  // I’ll handle any errors in that process
} 
finally {
  // I’ll fire either way
}

Let’s restructure the code above to use this syntax and catch any errors.

async function getQuote() {
  try {
    // get the type of quote from one fetch call, everything else waits for this to finish
    let quoteType = await fetch(`quotes.json`).then(res => res.json())

      // use what we got from the first call in the second call to an API, everything else waits for this to finish
    let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

    // finish up
    console.log('done')
  }

  catch(error) {
    console.warn(`We have an error here: ${error}`)
  }
}

getQuote()

We didn’t use finally here because we don’t always need it. It is a block that will always fire whether it is successful or fails. Consider using finally any time you’re duplicating things in both try and catch. I usually use this for some cleanup. I wrote an article about this, if you’re curious to know more.

You might eventually want more sophisticated error handling, such as a way to cancel an async function. There is, unfortunately, no way to do this natively, but thankfully, Kyle Simpson created a library called CAF that can help.

Further Reading

It's common for explanations of Async/Await to begin with callbacks, then promises, and use those explanations to frame Async/Await. Since Async/Await is well-supported these days, we didn’t walk through all of these steps. It’s still pretty good background, especially if you need to maintain older codebases. Here are some of my favorite resources out there:

The post Understanding Async Await appeared first on CSS-Tricks.

How to Increase Your Page Size by 1,500% with webpack and Vue

Disclaimer: This article is mostly satire. I do not think that I am better than you because I once wrote some TypeScript nor do I think that it’s a good thing for us to make web pages bigger. Feel free to misrepresent these views to maximize clicks.

You know, there are a lot of articles out there telling you how to make your page smaller: optimize your images, remove extraneous CSS rules, re-write the whole thing in Dreamweaver using framesets. Look,  Walmart just reduced their page size by some numbers, give or take.

What we don’t have are enough articles showing you how to increase your page size. In fact, the only article I could find was this one from the Geek Squad which ended up being about making the font size bigger. This is a good start, but I think we can do better.

Put on some weight

Now, why would you want to increase your page size? Isn’t that a not-very-nice thing for people on low bandwidth connections? Well, there are several excellent and in no-way-contrived reasons and here are three of them since things that come in threes are more satisfying.

  1. You have a gigabyte connection and you live in Tennessee so surely everyone else is in better shape than you are.
  2. Browsers do caching, silly. That means that you only have to download the site once. Stop complaining. First world problems.
  3. You don’t care whether or not people ever visit your site because you, "work to live, not live to work."

If any of those completely relatable reasons resonates with you, I’d like to show you how I increased the size of my CSS by 1,500% — and you can too, with one simple webpack trick.

One weird trick

It all started when I decided to refactor my retirement plan project called The Urlist over to the Bulma CSS framework.

The original incarnation of the site was all hand-rolled and my Sass looked like an episode of Hoarders.

"Burke, you don’t need 13 different .button styles. Why don’t you pick one and we can get rid of these other 12 so you have somewhere to sleep?"

Bulma also includes things like modals that I used third-party Vue components to make.

It also has a hamburger menu because it’s a well-known scientific fact that you cannot have a successful site without a hamburger.

Look, I don’t make the rules. This is just how business works.

I was quite happy with the result. Bulma styles are sharp, and the layout system is easy to learn. It’s almost as if someone somewhere understands CSS and also doesn’t hate me. That’s just a hard combination to find these days.

After a few weeks of refactoring (during which I would ask myself, "WHAT ARE YOU EVEN DOING MAN?!? THE SITE ALREADY WORKS!"), I finally finished. As a side note, the next time you think about refactoring something, don’t. Just leave it alone. If you don’t leave any technical debt for the next generation, they’re going to be extremely bored and that’s going to be on you.

When I built the project, I noticed something odd: the size of my CSS had gone up pretty significantly. My hand-crafted abomination was only 30KB gzipped and I was up to 260KB after the refactor.

And, to make matters worse, the Vue CLI was lecturing me about it...

Which, of course, I ignored. I don’t take instructions from robots.

What I did instead was deploy it. To production. On the internet. Because I did not spend all of this time refactoring to not deploy it. Yeah, sunk costs and all that, but excuse me if I’m more pragmatic than your poster of logical fallacies. All I’m saying is I came to party and I was not going home without a buzz.

Then I took to Twitter to announce my accomplishment to the ambivalent masses. As one does.

Shortly thereafter, Jeremy Thomas, who created Bulma (and clearly loves Dragon Ball) responded. It was quick, too. It’s like there is a bat signal that goes out whenever a moron tweets.

Duplicate styles? 13 times? What the heck is a namespace? Is that a π symbol or a custom Jeremy Thomas logo?

It’s at this moment that I realized that I have no idea what I’m doing.

Put the Sass down and back away slowly

I’ll be the first to admit that I don’t know a lot about CSS, and even Less about Sass. Get it? Less about Sass? Forget it. I don’t want your pity laugh.

When I setup my Vue CLI project to use Bulma, I created a src/styles folder and dropped in a bulma-but-not-all-of-bulma-only-some-of-it.scss file. They say naming things is hard, but I don’t see why.

That file imports the pieces of Bulma that I want to use. It’s Bulma, but not all of it. Only some of it.

@import "bulma/sass/utilities/_all.sass";
@import "bulma/sass/base/_all.sass";

@import "bulma/sass/form/shared.sass";
@import "bulma/sass/form/input-textarea.sass";

// etc...

Then I imported that file into a custom Sass file which I called... site.scss. I like to keep things simple.

@import "./bulma-but-not-all-of-bulma-only-some-of-it.scss";

html,
body {
  background-color: #f9fafc;
}

// etc...

I wanted to import these files into Vue globally so that I could use them in every component. And I wanted to do it the right way; the canonical way. I think it’s clear from my willingness to deploy 2+ MB of CSS into production that I like to do things the "right way".

I read this excellent blog post from Sarah Drasner called, "How to import a Sass file into every component in your Vue app." She shows how to do it by modifying the webpack build process via the vue.config.js file.

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        data: `@import "@/styles/site.scss";`
      }
    }
  }
}

What I did not understand is that this imports Sass into every component in a Vue app. You know, like the title of the blog post literally says. This is also how I ended up with a bunch of duplicate styles that had a data-v- attribute selector on them. I have scoped styles to thank for that.

How Vue handles `scoped`

Vue allows you to "scope" styles to a component. This means that a style only affects the component that it’s in, and not the rest of the page. There is no magic browser API that does this. Vue pulls it off by dynamically inserting a data- attribute in both the element and the selector. For example, this:

<template>
  <button class="submit">Submit</button>
<template>

<style lang="scss" scoped>
.submit {
  background-color: #20ae96;
}
</style>

...becomes this:

<button class="submit" data-v-2929>Submit</button>

<style>
.submit[data-v-2929] {
  background-color: #20ae96;
}
</style>

That dynamic data tag gets added to every child element in the component as well. So every element and every style for this component will have a data-v-2929 on them at runtime.

If you import a Sass file into your component that has actual styles in it, Vue (via webpack) will pull in those styles and "namespace" them with that dynamic data- attribute. The result is that is you include Bulma in your app 13 damn times with a bunch of data-v weirdness in front of it.

But this begs the question: if webpack renders the CSS in every single component, why would you ever want to use the vue.config.js approach? In a word: variables.

The variable sharing problem

You can’t define a Sass variable in one component and reference it from another. That would also be kind of hard to manage since you would be defining and using variables all over the place. Only I would write code like that.

You, on the other hand, would probably put all your variables in a variables.scss file. Each component would then reference that central store of variables. Importing a variables file into every single component is redundant. It’s also excessive. And unnecessary. And long-winded.

This is precisely the problem that Sarah’s article is solving: importing a Sass file into every component in your project.

It’s OK to import something like variables into every component because variables aren’t rendered. If you import 200 variables and only reference one of them, who cares? Those variables don’t exist in the rendered CSS anyway.

For example, this:

<style lang="scss" scoped>
$primary: #20ae96;
$secondary: #336699;

.submit {
  background-color: $primary
}
</style>

...becomes this:

<style>
.submit[data-v-2929] {
  background-color: #20ae96;
}
</style>

So, there are really two problems here:

  1. Bulma needs to be global.
  2. Bulma’s variables should be accessible from the components.

What we need is a clever combination of Sarah’s technique, along with a little proprietary knowledge about how Bulma is structured.

Using Bulma with the Vue

We’re going to accomplish this with the least amount of duplication by having three files in the src/styles directory:

variables.scss: This file will be where you pull in Bulma’s variables and override/define your own. Note that you have to include the following three files to get all of Bulma’s variables. And they have to be in this order...

// Your variables customizations go up here

// Include Bulma's variables
@import "bulma/sass/utilities/initial-variables.sass";
@import "bulma/sass/utilities/functions.sass";
@import "bulma/sass/utilities/derived-variables.sass";

bulma-custom.scss: This file is where you pull in the pieces of Bulma that you want. It should reference the variables.scss file.

@import "./variables.scss";

/* UTILTIES */
@import "bulma/sass/utilities/animations.sass";
@import "bulma/sass/utilities/controls.sass";
@import "bulma/sass/utilities/mixins.sass";

// etc...

site.scss: This pulls in the bulma-custom.scss file and is also where you define global styles that are used across the whole project.

@import url("https://use.fontawesome.com/releases/v5.6.3/css/all.css");
@import "./bulma-custom.scss";

html,
body {
  height: 100%;
  background-color: #f9fafc;
}

// etc...

Import the site.scss file into your main.js file. Or in my case, main.ts. Does it make me better than you that I use TypeScript? Yes. Yes it does.

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

// import styles
import "@/styles/site.scss";

This makes all of the Bulma pieces we are using available in every component. They are global, but only included once.

Per Sarah’s article, add the variables.scss file to the vue.config.js file.

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        data: `@import "@/styles/variables.scss";`
      }
    }
  }
}

This makes it so that you can reference any of the Bulma variables or your own from any .vue component.

Now you have the best of both worlds: Bulma is available globally and you still have access to all Bulma variables in every component.

Total size of CSS now? About 1,500% smaller...

Take that, Walmart.

Redemption via PR

In an effort to redeem myself, I’ve submitted a PR to the Bulma docs that walks through how to customize Bulma in a Vue CLI project. It’s an act of contrition for taking to Twitter and making Bulma seem like the problem when really Burke is the problem.

And you would think that by now I would have this figured out: Burke is always the problem.

The post How to Increase Your Page Size by 1,500% with webpack and Vue appeared first on CSS-Tricks.

How Frontend Developers Can Help To Bridge The Gap Between Designers And Developers

How Frontend Developers Can Help To Bridge The Gap Between Designers And Developers

How Frontend Developers Can Help To Bridge The Gap Between Designers And Developers

Stefan Kaltenegger

Within the last nine years, almost every designer I used to work with expressed their frustration to me about them frequently having to spend days giving feedback to developers to correct spacings, font sizes, visual as well as layout aspects that had simply not been implemented correctly. This often lead to weakening the trust between designers and developers, and caused unmotivated designers along with a bad atmosphere among the two disciplines.

A lot of times developers still seem to have the bad reputation of being overly technical and ignorant when it comes to being considerate about details the design team came up with. According to an article by Andy Budd, “[…] a lot of developers are in the same position about design — they just don’t realize it.” In reality though (as Paul Boag points out), “developers [need to] make design decisions all the time.”

In this article, I’ll provide practical points of advice for frontend developers to avoid frustration and increase productivity when working with their creative counterpart.

Looking Through The Eyes Of A Designer

Let’s for one moment imagine you were a designer and spent the last weeks — if not months — to work out a design for a website. You and your teammates went through multiple internal revisions as well as client presentations, and put a solid effort into fine-tuning visual details such as white space, font styles, and sizes. (In a responsive era — for multiple screen sizes, of course.) The designs have been approved by the client and were handed off to the developers. You feel relieved and happy.

A few weeks later, you receive an email from your developer that says:

“Staging site is set up. Here’s the link. Can you please QA?”

In a thrill of anticipation, you open that staging link and after scrolling through some of the pages, you notice that the site looks a little off. Spacings are not even close to what your design suggested and you notice some kinks in the layout: wrong font faces and colors as well as incorrect interactions and hover states. Your excitement starts to slowly fade and turn into a feeling of frustration. You can’t help but ask yourself, “How could that have happened?”

The Search For Reasons

Maybe there were just a lot of unfortunate misunderstandings in the communication between the designers and developers. Nevertheless, you continue asking yourself:

  • What did the the handover of designs look like? Were there just some PDFs, Photoshop or Sketch files shared via e-mail with some comments, or was there an actual handover meeting in which various aspects such as a possible design system, typography, responsive behavior, interactions and animations were discussed?
  • Did interactive or motion prototypes that help to visualize certain interactions exist?
  • Was a list of important aspects with defined levels of priority created?
  • How many conversations took place — with both designers and developers in the same room together?

Since communication and handover are two very important key points, let’s take a closer look at each.

Communication Is Key

Designers and developers, please talk to each other. Talk a lot. The earlier on in the project and the more often, the better. If possible, review design work in progress together early in the project (and regularly) in order to constantly evaluate feasibility and get cross-disciplinary input. Designers and developers naturally both focus on different aspects of the same part and therefore see things from different angles and perspectives.

Checking in early on lets developers become familiarized with the project so they can start researching and planning ahead on technical terms and bring in their ideas on how to possibly optimize features. Having frequent check-ins also brings the team together on a personal and social level, and you learn how to approach each other to communicate effectively.

The Handover From Design To Development

Unless an organization follows a truly agile workflow, an initial handover of design comps and assets (from the design team to the developers) will likely happen at some point in a project. This handover — if done thoroughly — can be a solid foundation of knowledge and agreements between both sides. Therefore, it is essential not to rush through it and plan some extra time.

Ask a lot of questions and talk through every requirement, page, component, feature, interaction, animation, anything — and take notes. If things are unclear, ask for clarification. For example, when working with external or contract-based teams, both designers and developers can sign off the notes taken as a document of mutual agreement for future reference.

Flat and static design comps are good for showing graphical and layout aspects of a website but obviously lack the proper representation of interactions and animations. Asking for prototypes or working demos of complex animations will create a clearer vision of what needs to be built for everyone involved.

Nowadays, there’s is a wide range of prototyping tools available that designers can utilize to mockup flows and interactions in different levels of fidelity. Javier Cuello explains how to choose the right prototyping tool for your project in one of his comprehensive articles.

Every project is unique, and so are its requirements. Due to these requirements, not all conceptualized features can always be built. Often the available time and resources to build something can be a limiting factor. Furthermore, constraints can come from technical requirements such as feasibility, accessibility, performance, usability and cross-browser support, economic requirements like budget and license fees or personal constraints like the skill level and availability of developers.

So, what if these constraints cause conflicts between designers and developers?

Finding Compromises And Building Shared Knowledge

In order to successfully ship a project on time and meet all defined requirements, finding compromises between the two disciplines is mostly inevitable. Developers need to learn to speak to designers in non-technical terms when they explain reasons why things need changes or can’t be built in a specific situation.

Instead of just saying, “Sorry, we can’t build this,” developers should try to give an explanation that is understandable for designers and — in the best case — prepare suggestions for an alternative solution that works within the known constraints. Backing your point with statistics, research, or articles, can help to emphasize your argument. Also, if timing is an issue, maybe the implementation of some time-consuming parts can be moved to a later phase of the project?

Even though it is not always possible, having designers and developers sit next to each other can shorten feedback loops and make it easier to work out a compromised solution. Adapting and prototyping can be done directly through coding and optimizing with DevTools open.

Show your fellow designers how to use DevTools in a browser so that they can alter basic information and preview small changes in their browser (e.g. paddings, margins, font sizes, class names) on the fly.

If the project and team structure allow it, building and prototyping in the browser as soon as possible can give everyone involved a better understanding of the responsive behavior and can help eliminate bugs and errors in the early stage of the project.

The longer designers and developers work together, the better designers will understand what is easier and what is more difficult for the developers to build. Over time, they can eventually refer to solutions that have worked for both sides in the past:

“We’ve used that solution to find a compromise in Project A. Can we use it for this project as well?”

This also helps developers get a better sense of what details the designers are very specific about and what visual aspects are important to them.

Designers Expect The Frontend To Look (And Function) Like Their Design

The Design File Vs. Browser Comparison

A helpful technique to prevent designers from frustration is to make a simple left-right comparison between the design file you got handed over and what your current state of development looks like. This might sound trivial, but as a developer, you have to take care of so many things that need to function under the hood that you might have missed some visual details. If you see some noticeable discrepancies, simply correct them.

Think of it this way: Every detail in your implementation that looks exactly as it was designed saves both you and the designer valuable time and headaches, and encourages trust. Not everyone might have the same level of attention to detail, but in order to train your eye to notice visual differences, a quick round of Can’t Unsee might be a good help.

“Can’t Unsee” is a game where you need to choose the most correct design out of two choices.
(Image credits: Can’t Unsee) (Large preview)

This nostalgically reminds me of a game we used to play a long time ago called “Find it”. You had to find discrepancies by comparing two seemingly similar images in order to score points.

In “Find it”, players have to find errors comparing two images
(Image credits: Mordillo find them) (Large preview)

Still, you may be thinking:

“What if there simply is no noticeable system of font sizes and spacings in the design?”

Well, good point! Experience has shown me that it can help to start a conversation with the designer(s) by asking for clarification rather than radically starting to change things on your own and creating unwanted surprises for the designer(s) later.

Learn Basic Typographic And Design Rules

As Oliver Reichenstein states in one of his articles, 95% of the information on the web is written language. Therefore, typography plays a vital role not only in web design but also in development. Understanding basic terms and concepts of typography can help you communicate more effectively with designers, and will also make you more versatile as a developer. I recommend reading Oliver’s article as he elaborates the importance of typography on the web and explains terms such as micro- and macro-typography.

In the “Reference Guide For Typography In Mobile Web Design”, Suzanne Scacca thoroughly covers typography terminology such as typeface, font, size, weight, kerning, leading and tracking as well as the role of typography in modern web design.

If you would like to further expand your typographical horizon, Matthew Butterick’s book “Butterick’s Practical Typography” might be worth reading. It also provides a summary of key rules of typography.

One thing I found particularly useful in responsive web design is that one should aim for an average line length (characters per line) of 45 to 90 characters since shorter lines are more comfortable to read than longer lines.

Comparing two text paragraphs with different line lengths
Comparing different line lengths (Large preview)

Should Developers Design?

There has been a lot of discussion whether designers should learn to code, and you may be asking yourself the same question the other way around. I believe that one can hardly excel in both disciplines, and that’s totally fine.

Rachel Andrew nicely outlines in her article “Working Together: How Designers And Developers Can Communicate To Create Better Projects” that in order to collaborate more effectively, we all need to learn something of the language, skills, and priorities of our teammates so that we can create a shared language and overlapping areas of expertise.

One way to become more knowledgable in the field of design is an online course known as “Design for Developers” that is offered by Sarah Drasner in which she talks about basic layout principles and color theory — two fundamental areas in web design.

“The more you learn outside of your own discipline, is actually better for you [...] as a developer.”

— Sarah Drasner

The Visual Center

By collaborating with designers, I learned the difference between the mathematical and visual center. When we want to draw the reader’s attention to a certain element, our eye’s natural focal point lies just slightly above the mathematical center of the page.

We can apply this concept, for example, to position modals or any kinds of overlays. This technique helps us to naturally get the user’s attention and makes the design appear more balanced:

Comparing two page layouts where one shows a text aligned to the mathematical and the other a text aligned to the visual center
(Large preview)

We’re All In This Together

In fast-paced and not-so-agile agency environments with tight deadlines, developers are often asked to implement fully functional responsive frontends based on a mobile and desktop mockup. This inevitably forces the developer to take design decisions throughout the process. Questions such as, “At what width will we decrease the font size of headlines?” or “When should we switch our three-column layout to a single column?” may arise.

Also, in the heat of the moment, it may happen that details like error states, notifications, loading states, modals or styles of 404 pages simply fall through the cracks. In such situations, it’s easy to start finger-pointing and blaming the people who should have thought about this earlier on. Ideally, developers shouldn’t ever be put in such a situation, but what if that’s the case?

When I listened to Ueno’s founder and CEO, Haraldur Thorleifsson, speak at a conference in San Francisco in 2018, he presented two of their core values:

“Nothing here is someone else’s problem.”
“We pick up the trash we didn’t put down.”

What if more developers proactively start mocking-up the above-mentioned missing parts as good as they can in the first place, and then refine together with the designer sitting next to them? Websites live in the browser, so why not utilize it to build and refine?

While winging missing or forgotten parts might not always be ideal, I’ve learned in my past experiences that it has always helped us to move forward faster and eliminate errors on the fly — as a team.

Of course, this does not mean that designers should be overruled in the process. It means that developers should try to respectfully meet designers halfway by showing initiative in problem-solving. Besides that, I as a developer was valued way more by the team simply for caring and taking on responsibility.

Building Trust Between Designers And Developers

Having a trustful and positive relationship between the creative and tech team can strongly increase productivity and outcome of work. So what can we, as developers, do to increase trust between the two disciplines? Here are a few suggestions:

  1. Show an eye for details.
    Building things exactly as they were designed will show the designers that you care and put a big smile on their faces.
  2. Communicate with respect.
    We’re all human beings in a professional environment striving for the best possible outcome. Showing respect for each other’s discipline should be the basis for all communication.
  3. Check in early on and regularly.
    Involving developers from the start can help to eliminate errors early on. Through frequent communication, team members can develop a shared language and better understanding of each other’s positions.
  4. Make yourself available.
    Having at least an optional 30-minute window a day when designers can discuss ideas with developers can give designers a feeling of being supported. This also gives developers the opportunity to explain complex technical things in words that not-so-technical people can understand better.

The Result: A Win-Win Situation

Having to spend less time in QA through effective communication and a proper handover of designs gives both the creative and dev team more time to focus on building actual things and less headaches. It ultimately creates a better atmosphere and builds trust between designers and developers. The voice of frontend developers that show interest and knowledge in some design-related fields will be heard more in design meetings.

Proactively contributing to finding a compromise between designers and developers and problem-solving as a developer can give you a broader sense of ownership and involvement with the whole project. Even in today’s booming creative industry, it’s not easy to find developers who — besides their technical skillset — care about and have an eye for visual details. This can be your opportunity to help bridge the gap in your team.

Smashing Editorial (dm, yk, il)

A Site for Front-End Development Conferences (Built with 11ty on Netlify)

I built a new little site! It's a site for listing upcoming conferences in the world of front-end web design and development. In years past (like 2017), Sarah Drasner took up this daunting job. We used a form for new conference submissions, but it was still a rather manual task of basically manually editing a blog post. I wanted to keep doing this, as I think it's valuable to have a simple reference page for conferences in our niche slice of the web, but I wanted the site to be able to live on year after year with lower maintenance-related technical debt.

So this is what I did!

I wanted to get it on GitHub.

So I put it there. Part of the beauty of GitHub is that it opens up the idea of collaboration through pull requests to really anyone in the world. You need to have a GitHub account, but that's free, and you need to understand Git at least on some minor level (which is a barrier that I'd like to resolve in time), but it invites more collaboration than something like just asking people to email you content and ideas.

I wanted the content in Markdown in the Repo.

The Front Matter format, which is Markdown with some data the the top, is such a useful and approachable format. You need almost zero knowledge, not even HTML, to be able to create/edit a file like this:

Having the actual conference data in the repo means that pull requests aren't just for design or features; more commonly, they will be for actual conference data. The work of making this site full of all the best conferences is the work of all of us, not just one of us.

At the time of this writing there have already been 30 closed pull requests.

I used 11ty to build the site.

11ty is almost fascinatingly simple. It looks in one directory for what it needs to process or move to another directory. It supports my favorite templating system out of the box: Nunjucks. Plus front matter Markdown like I mentioned above.

I was able to essentially design a card that displays the data we get from the Markdown files, and then build the homepage of the site by looping over those Markdown files and applying the templated card.

11ty is based on Node.js, so while I did have some learning-curve moments, it was comfortable for me to work in. There definitely is configuration for doing the things I wanted to be doing. For example, this is how I had to make a "collection" of conferences in order to loop over them:

config.addCollection("conferences", function(collection) {
  let allConferences = collection.getFilteredByGlob("site/conferences/*.md");
  let futureConferences = allConferences.filter(conf => {
    return conf.data.date >= new Date();
  });
  return futureConferences;
});

The site is hosted on Netlify.

One reason to use Netlify here is that it's incredibly easy. I made a site site in Netlify by connecting it to the GitHub repo. I told it how to build the site (it's a single command: eleventy) and where the built site files are (dist), and that's it. In fact, that's even part of the repo:

Now whenever I push to the master branch (or accept a pull request into master), the site automatically rebuilds and deploys. Just takes seconds. It's really amazing.

Better, for each pull request, Netlify makes sure everything is in order first:

My favorite is the deploy preview. It gives you an (obscure) URL that will literally last forever (immutable) and that serves as a look at the built version of this site with that pull request.

So, not only is it extremely easy to use Netlify, but I get a bunch of stuff for free, like the fact that the site is smokin' fast on their CDNs and such.

I'm also excited that I've barely tapped into Netlify's features here, so there is a lot of stuff I can dig into over time. And I intend to!

I use Zapier to re-build the site every day.

There is a bit of a time-sensitive nature to this site. The point of this site is to reference it for upcoming conferences. It's less interesting to see past conferences (although maybe we can have a browse-able archive in the future). I like the idea of ripping off past conferences for the homepage. If this was PHP (or whatever), we could do that at runtime, but this is a static site (on purpose). Doing something like this at build time is no big deal (see that code snippet above that only returns conferences past today's date). But we can't just waiting around for pull requests to re-build the site, nor do I want to make it a manual thing I need to do every day.

Fortunately, this is easy as pie with Zapier:

Phil Hawksworth took this to the extreme once and built a clock website that rebuilds every minute.


This site wasn't just an experiment. I'd like to keep it going! If you're part of running a conference, I'm quite sure it doesn't hurt to add it to add yours, just so long as it has an enforcable and actionable Code of Conduct, and is within the world of front-end web design and development.

The post A Site for Front-End Development Conferences (Built with 11ty on Netlify) appeared first on CSS-Tricks.

Animating Between Views in React

You know how some sites and web apps have that neat native feel when transitioning between two pages or views? Sarah Drasner has shown some good examples and even a Vue library to boot.

These animations are the type of features that can turn a good user experience into a great one. But to achieve this in a React stack, it is necessary to couple crucial parts in your application: the routing logic and the animation tooling.

Let’s start with animations. We’ll be building with React, and there are great options out there for us to leverage. Notably, the react-transition-group is the official package that handles elements entering and leaving the DOM. Let’s explore some relatively straightforward patterns we can apply, even to existing components.

Transitions using react-transition-group

First, let’s get familiar with the react-transition-group library to examine how we can use it for elements entering and leaving the DOM.

Single components transitions

As a simple example of a use case, we can try to animate a modal or dialog — you know, the type of element that benefits from animations that allow it enter and leave smoothly.

A dialog component might look something like this:

import React from "react";

class Dialog extends React.Component {
  render() {
    const { isOpen, onClose, message } = this.props;
    return (
      isOpen && (
        <div className="dialog--overlay" onClick={onClose}>
          <div className="dialog">{message}</div>
        </div>
      )
    );
  }
}

Notice we are using the isOpen prop to determine whether the component is rendered or not. Thanks to the simplicity of the recently modified API provided by react-transition-group module, we can add a CSS-based transition to this component without much overhead.

First thing we need is to wrap the entire component in another TransitionGroup component. Inside, we keep the prop to mount or unmount the dialog, which we are wrapping in a CSSTransition.

import React from "react";
import { TransitionGroup, CSSTransition } from "react-transition-group";

class Dialog extends React.Component {
  render() {
    const { isOpen, onClose, message } = this.props;
    return (
      <TransitionGroup component={null}>
        {isOpen && (
          <CSSTransition classNames="dialog" timeout={300}>
            <div className="dialog--overlay" onClick={onClose}>
              <div className="dialog">{message}</div>
            </div>
          </CSSTransition>
        )}
      </TransitionGroup>
    );
  }
}

Every time isOpen is modified, a sequence of class names changes will happen in the dialog’s root element.

If we set the classNames prop to "fade", then fade-enter will be added immediately before the element mounts and then fade-enter-active when the transition kicks off. We should see fade-enter-done when the transition finishes, based on the timeout that was set. Exactly the same will happen with the exit class name group at the time the element is about to unmount.

This way, we can simply define a set of CSS rules to declare our transitions.

.dialog-enter {
  opacity: 0.01;
  transform: scale(1.1);
}

.dialog-enter-active {
  opacity: 1;
  transform: scale(1);
  transition: all 300ms;
}

.dialog-exit {
  opacity: 1;
  transform: scale(1);
}

.dialog-exit-active {
  opacity: 0.01;
  transform: scale(1.1);
  transition: all 300ms;
}

JavaScript Transitions

If we want to orchestrate more complex animations using a JavaScript library, then we can use the Transition component instead.

This component doesn’t do anything for us like the CSSTransition did, but it does expose hooks on each transition cycle. We can pass methods to each hook to run calculations and animations.

<TransitionGroup component={null}>
  {isOpen && (
    <Transition
      onEnter={node => animateOnEnter(node)}
      onExit={node => animateOnExit(node)}
      timeout={300}
    >
      <div className="dialog--overlay" onClick={onClose}>
        <div className="dialog">{message}</div>
      </div>
    </Transition>
  )}
</TransitionGroup>

Each hook passes the node to the callback as a first argument — this gives control for any mutation we want when the element mounts or unmounts.

Routing

The React ecosystem offers plenty of router options. I’m gonna use react-router-dom since it’s the most popular choice and because most React developers are familiar with the syntax.

Let’s start with a basic route definition:

import React, { Component } from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import Home from '../views/Home'
import Author from '../views/Author'
import About from '../views/About'
import Nav from '../components/Nav'

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <div className="app">
          <Switch>
            <Route exact path="/" component={Home}/>
            <Route path="/author" component={Author} />
            <Route path="/about" component={About} />
          </Switch>
        </div>
      </BrowserRouter>
    )
  }
}

We want three routes in this application: home, author and about.

The BrowserRouter component handles the browser’s history updates, while Switch decides which Route element to render depending on the path prop. Here’s that without any transitions:

Don’t worry, we’ll be adding in page transitions as we go.

Oil and water

While both react-transition-group and react-router-dom are great and handy packages for their intended uses, mixing them together can break their functionality.

For example, the Switch component in react-router-dom expects direct Route children and the TransitionGroup components in react-transition-group expect CSSTransition or Transition components to be direct children of it too. So, we’re unable to wrap them the way we did earlier.

We also cannot toggle views with the same boolean approach as before since it’s handled internally by the react-router-dom logic.

React keys to the rescue

Although the solution might not be as clean as our previous examples, it is possible to use the libraries together. The first thing we need to do is to move our routes declaration to a render prop.

<BrowserRouter>
  <div className="app">
    <Route render={(location) => {
      return (
        <Switch location={location}>
          <Route exact path="/" component={Home}/>
          <Route path="/author" component={Author} />
          <Route path="/about" component={About} />
        </Switch>
      )}
    />
</BrowserRouter>

Nothing has changed as far as functionality. The difference is that we are now in control of what gets rendered every time the location in the browser changes.

Also, react-router-dom provides a unique key in the location object every time this happens.

In case you are not familiar with them, React keys identify elements in the virtual DOM tree. Most times, we don’t need to indicate them since React will detect which part of the DOM should change and then patch it.

<Route render={({ location }) => {
  const { pathname, key } = location

  return (
    <TransitionGroup component={null}>
      <Transition
        key={key}
        appear={true}
        onEnter={(node, appears) => play(pathname, node, appears)}
        timeout={{enter: 750, exit: 0}}
      >
        <Switch location={location}>
          <Route exact path="/" component={Home}/>
          <Route path="/author" component={Author} />
          <Route path="/about" component={About} />
        </Switch>
      </Transition>
    </TransitionGroup>
  )
}}/>

Constantly changing the key of an element — even when its children or props haven't been modified — will force React to remove it from the DOM and remount it. This helps us emulate the boolean toggle approach we had before and it’s important for us here because we can place a single Transition element and reuse it for all of our view transitions, allowing us to mix routing and transition components.

Inside the animation function

Once the transition hooks are called on each location change, we can run a method and use any animation library to build more complex scenes for our transitions.

export const play = (pathname, node, appears) => {
  const delay = appears ? 0 : 0.5
  let timeline

  if (pathname === '/')
    timeline = getHomeTimeline(node, delay)
  else
    timeline = getDefaultTimeline(node, delay)

  timeline.play()
}

Our play function will build a GreenSock timeline here depending on the pathname, and we can set as many transitions as we want for each different routes.

Once the timeline is built for the current pathname, we play it.

const getHomeTimeline = (node, delay) => {
  const timeline = new Timeline({ paused: true });
  const texts = node.querySelectorAll('h1 > div');

  timeline
    .from(node, 0, { display: 'none', autoAlpha: 0, delay })
    .staggerFrom(texts, 0.375, { autoAlpha: 0, x: -25, ease: Power1.easeOut }, 0.125);

  return timeline
}

Each timeline method digs into the DOM nodes of the view and animates them. You can use other animation libraries instead of GreenSock, but the important detail is that we build the timeline beforehand so that our main play method can decide which one should run for each route.

Success!

I’ve used this approach on lots of projects, and though it doesn't present obvious performance issues for inner navigations, I did notice a concurrency issue between the browser's initial DOM tree build and the first route animation. This caused a visual lag on the animation for the first load of the application.

To make sure animations are smooth in each stage of the application, there’s one last thing we can do.

Profiling the initial load

Here’s what I found when auditing the application in Chrome DevTools after a hard refresh:

You can see two lines: one blue and one red. Blue represents the load event and red the DOMContentLoaded. Both intersect the execution of the initial animations.

These lines are indicating that elements are animating while the browser hasn’t yet finished building the entire DOM tree or it's parsing resources. Animations account for big performance hits. If we want anything else to happen, we’d have to wait for the browser to be ready with these heavy and important tasks before running our transitions.

After trying a lot of different approaches, the solution that actually worked was to move the animation after these events — simple as that. The issue is that we can’t rely on event listeners.

window.addEventListener(‘DOMContentLoaded’, () => {
  timeline.play()
})

If for some reason, the event occurs before we declare the listener, the callback we pass will never run and this could lead to our animations never happening and an empty view.

Since this is a concurrency and asynchronous issue, I decided to rely on promises, but then the question became: how can promises and event listeners be used together?

By creating a promise that gets resolved when the event takes place. That’s how.

window.loadPromise = new Promise(resolve => {
  window.addEventListener(‘DOMContentLoaded’, resolve)
})

We can put this in the document head or just before the script tag that loads the application bundle. This will make sure the event never happens before the Promise is created.

Plus, doing this allows us to use the globally exposed loadPromise to any animation in our application. Let’s say that we don’t only want to animate the entry view but a cookie banner or the header of the application. We can simply call each of these animations after the promise has resolved using then along with our transitions.

window.loadPromise.then(() => timeline.play())

This approach is reusable across the entire codebase, eliminating the issue that would result when an event gets resolved before the animations run. It will defer them until the browser DOMContentLoaded event has passed.

See now that the animation is not kicking off until the red line appears.

The difference is not only on the profiling report — it actually solves an issue we had in a real project.

Wrapping up

In order to act as reminders, I created a list of tips for me that you might find useful as you dig into view transitions in a project:

  • When an animation is happening nothing else should be happening. Run animations after all resources, fetching and business logic have completed.
  • No animation is better than crappy animations If you can’t achieve a good animation, then removing it is a fair sacrifice. The content is more important and showing it is the priority until a good animation solution is in place.
  • Test on slower and older devices. They will make it easier for you to catch spots with weak performance.
  • Profile and base your improvements in metrics. Instead of guessing as you go, like I did, see if you can spot where frames are being dropped or if something looks off and attack that issue first.

That’s it! Best of luck with animating view transitions. Please post a comment if this sparked any questions or if you have used transitions in your app that you’d like to share!

The post Animating Between Views in React appeared first on CSS-Tricks.