​Learn UI Design: The Complete Video Course

(This is a sponsored post.)

If you’ve ever thought “Man, all my designs look like crap”, this may be the best ad you see all day. If you’ve desperately searched Dribbble or Behance for inspiration, yet found yourself completely unable to make something look nice, this one’s for you. And if you’ve ever had a sinking feeling that most design articles are worthless, and no matter how much you read about color theory, it’s not going to make your bad designs look good, well, let’s talk.

Learn UI Design is an online video course to take you from design newbie to being able to confidently create beautiful designs for any site or app. From color to typography, icons to process, Learn UI Design covers every aspect of interface design. Enrollment is open for 2 weeks only.

I should introduce myself. I’m Erik Kennedy. I’m an independent designer, I’ve traveled the globe designing sites and apps for companies big and small (like Soylent and Amazon), and my design writing has been read by over a million people (you might know me from this article). Yet I started out as a developer who couldn’t create nice-looking software to save his life. Sure, I developed some applications for work, created a few websites and side projects at home, even tried my hand at a nights-and-weekend startup. But there was an issue: everything I made looked like crap.

Design was something I was always interested in, but never great at. I knew what I liked, but I didn’t know how to create such a design. Consequently, everything I did had One-Man-Project syndrome: it looked like it was made by someone in their spare time – not professional, not considered, not worth the download, not worth the purchase.

In the end, I learned design the same way I’ve learned any creative endeavor: cold, hard analysis. And shameless copying of what’s worked. I’ve worked 10 hours on a UI project and billed for 1. The other 9 were the wild flailing of learning. Desperately searching Dribbble and Behance and Pinterest for some idea of how to make my awful design better.

That was the beginning, anyhow. Over time, I built up a toolset of hacks and heuristics. I was tired of reading design articles that failed the fundamental test of any skill tutorial: it didn’t help me improve what I was working on then and there. My gold standard was to find what worked. What made a difference between ugly and gorgeous. Over the years, I built up these tools across all areas of user interface design – color, typography, iconography, and so on.

Today, Learn UI Design has hundreds of happy students, and the course is used and regarded by folks like Chris Coyier…

…and Jeremiah Shoaf (the founder of Typewolf).

And of course plenty of other mere mortals:

Here’s a peek at the syllabus:

  • Begin here (11:10)
  • Setting Up Sketch & Asset Files for UI Design (15:36)
  • How to Build Your Design Gut Instinct (16:26)
  • 3 Methods for Designing Above Your Level (10:44)
  • Finding & Using Design Inspiration (20:46)
  • Analyzing Aesthetics (17:18)
  • Alignment (36:32)
  • Spacing (52:12)
  • Lighting & Shadows (32:28)
  • Grids (25:37)
  • Consistency (34:19)
  • Introduction to HSB (13:32)
  • Luminosity (20:00)
  • Gray: The Most Important Color (27:32)
  • Adjustment: The Most Important Color Skill (34:46)
  • 3 Ways to Fix Clashing Colors (9:09)
  • Picking a Primary UI Color (10:33)
  • Picking Secondary UI Colors (47:04)
  • Dark Interfaces (22:41)
  • Gradients (27:15)
  • Terminology: The Bare Minimum
  • Choosing Fonts (53:42)
  • Good Fonts Table
  • Styling Text (44:24)
  • Styling Text 2 (35:56)
  • Pairing Fonts (50:35)
  • 7 Methods for Overlaying Text on Images (21:13)
  • Form Controls (42:38)
  • Icons 1: Vector Editing (30:46)
  • Icons 2: Icon Design (52:39)
  • Photography & Imagery (39:34)
  • Lists & Tables (41:49)
  • Responsive UI Design (48:54)
  • Designing Multi-State Screens (38:32)
  • Creating a Design Portfolio (33:07)
  • Finding Clients (18:29)
  • Presenting & Getting Good Feedback on Your Designs (34:17)

For those of you keeping score at home, that’s 35 videos totaling almost 20 hours of content.

Sign up now and get:

  • Immediate access to the full video curriculum
  • Dozens of multimedia resources, downloads, and homework assignments
  • Access to the Learn UI Design Slack community, where you can get feedback, design reviews, and more design resources

Comes with a no-questions-asked, 30-day money back guarantee.

Learn UI Design is open for enrollments now through March 14th.

» Enroll now

Direct Link to Article — Permalink

​Learn UI Design: The Complete Video Course is a post from CSS-Tricks

Responsive Components: a Solution to the Container Queries Problem

Container Queries, as in, the ability to style elements based on values from a particular element, like its width and height. We have media queries, but those are based on the viewport not individual elements. There are plenty of use cases for them. It’s been said before, but I’ll say it again, if container queries existed, the vast majority of media queries in CSS would actually be container queries.

Discussion about how to pull it off technologically gets interesting. In my mind, ideally, we get this ability right in CSS. The trouble with doing it this way is one of circularity. Not even in regards to being able to write CSS that triggers a scenario in which the query doesn’t match anymore, which is tricky enough, but literally changing the long-standing single-pass way in which browsers render a page.

There are plenty of takes at solving this. All JavaScript of course. Dave Rupert rounded some of them up here. They are all a bit different.

Seems to me the most well-suited JavaScript API for this is ResizeObserver. It’s Chrome-only as I write, but here’s a chart that should stay updated in time:

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.


Chrome Opera Firefox IE Edge Safari
64 No No No No No

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
No No No No No No

That was a heck of a lot of words to intro the fact that Philip Walton just wrote a hell of an article about doing just this. The core of it is that you use ResizeOveserver to toss classes onto elements, then style them with those classes. He concludes:

The strategy outlined in this article:

  • Will work, today, on any website
  • Is easy to implement (copy/paste-able)
  • Performs just as well as a CSS-based solution
  • Doesn’t require any specific libraries, frameworks, or build tools.
  • Leverages progressive enhancement, so users on browser that lack the required APIs or have JavaScript disabled can still use the site.

The browser support for ResizeObserver is a little scary, but it’s such a nice API I would expect more widespread support sooner than later.

Direct Link to Article — Permalink

Responsive Components: a Solution to the Container Queries Problem is a post from CSS-Tricks

Using Sass to Control Scope With BEM Naming

Controlling scope is something you probably don’t tend to consider when working with CSS and Sass. We have had access to the ampersand (&) for quite some time now, which gives us a level of scope—but it’s easy for it to lose its usefulness when you’re nested quite deeply. The & can send us down a windy road of doom when we completely lose track of what it means and can be responsible for some really heavy bloat.

Meanwhile, in JavaScript, we can control the scope of this by casting it as a variable at the point where it means what we will want it to. This is often at the start of a method or object:

function foo() { let self = this; return function() { // Self = foo(), even int his closure return self; }
} // We need to instantiate foo() or its 'this' will be the window
let bar = new foo();

Now that we have self in the above context, we always have a reference to foo() regardless of where we might find ourselves within the function. This is really useful when we end up in setTimeout scenarios, closures or event handlers.

A quick example of an event will hopefully help to illustrate my point.

Using this markup:

<div class="component"> <div class="component__child-element"></div>
<div class="component"> <div class="component__child-element"></div>

We can add this JavaScript:

function foo() { // Load up all component child elements let childElements = [...document.querySelectorAll('.component__child-element')]; // Loop each element and add an event listener childElements.map(element => { element.addEventListener('click', function(evt) { // Log what `this` currently is console.log(this); }); });
} // Create a new instance of foo
let bar = new foo();

In that code sample, if you click a component__child-element with your console open, this will report itself as the event target, which happens to be the element that we clicked. This isn’t ideal if you thought it would reference foo().

Now, if we run the same sample with self in place of this in the event handler, the console will report an instance of foo() instead:

function foo() { // Control scope of this by storing it let self = this; // Load up all component child elements let childElements = [...document.querySelectorAll('.component__child-element')]; // Loop each element and add an event listener childElements.map(element => { element.addEventListener('click', function(evt) { // Self will report itself as `foo()` console.log(self); }); });
} // Create a new instance of foo
let bar = new foo();

So, how does this relate to Sass?

I’m glad you stuck with me there, because that JavaScript example was a primer for what we’re going to look at with Sass.

First, let’s start with that same markup and some core styles.

<div class="component"> <div class="component__child-element"></div>
<div class="component"> <div class="component__child-element"></div>
.component { display: block; max-width: 30rem; min-height: 30rem; margin: 5rem auto; background: rebeccapurple; position: relative; border: 1px dashed rebeccapurple; &__child-element { display: block; width: 15rem; height: 15rem; border-radius: 50%; position: absolute; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); background: white; }

This gives us a nice purple square with a white circle inside it. Nothing groundbreaking, but useful for this example.

See the Pen Square with Circle by Geoff Graham (@geoffgraham) on CodePen.

You’ll probably notice that we’re using BEM syntax, so let’s go ahead and create a modifier.

We want an inverted version of .component that has a white background with a purple circle in it. So, let’s go ahead and approach it how we’d probably think to do it straight away by including it in the same nested ruleset:

.component { // Other styles redacted for brevity // The reversed modifier flips the colors around &--reversed { background: white; border-color: lightgray; &__child-element { background: rebeccapurple; } }

See the Pen Square with Circle by Geoff Graham (@geoffgraham) on CodePen.

Wait, why is this not working? The problem is that the & has a scope of .component--reversed, so &__child-element compiles to .component--reversed__child-element, which doesn’t exists in the markup.

$self to the rescue!

Just like in the JavaScript we looked at before, we’re going to cast our initial scope into a variable called $self. You can do it like this:

.component { $self: &;

That means that wherever we use $self in our component, it will compile to .component.

So let’s refactor that reversed modifier, so that it actually works:

.component { $self: &; // Hey look, it's our new best friend! display: block; max-width: 30rem; min-height: 30rem; // Other styles redacted &--reversed { background: white; border-color: lightgray; // Here, we use $self to get the correct scope #{ $self }__child-element { background: rebeccapurple; } }

The compiled CSS for that element is now .component--reversed .component__child-element which of course exists and successfully turns the inner circle purple:

See the Pen ‘Using Sass to Control Scope With BEM Naming’ example by Andy Bell (@hankchizljaw) on CodePen.

Further exploration

One thing I like to do in my components is reference a parent’s modifier within the element itself, rather than referencing the element within the modifier like we did earlier. This is to keep my styles grouped per element. For the same reason, I’ll also put media queries within elements too.

I end up with code that looks like this:

// We're still within .component where $self has been declared.
&__child-element { display: block; width: 15rem; height: 15rem; border-radius: 50%; position: absolute; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); background: white; // Flip the color when the parent is reversed #{ $self }--reversed & { background: rebeccapurple; }

Having access to $self as the root context gives us the flexibility to approach our modifiers like that with ease. Because $self is .component and & is .component__element, adding the --reversed portion generates us .component--reversed .component__element which is exactly what we wanted.

See the Pen ‘Using Sass to Control Scope With BEM Naming’ example by Andy Bell (@hankchizljaw) on CodePen.

A more advanced example

Now let’s really push the boat out and write something interesting. We’re going to layout three separate grids. Using $self, I’m going to change the color of the grid items using a sibling selector within a grid-item itself.

See the Pen ‘Using Sass to Control Scope With BEM Naming’ example by Andy Bell (@hankchizljaw) on CodePen.

Let’s look at the Sass that affects the color:

&__item { // Item styles redacted for brevity // For each direct `.grid sibling`, make this item rebeccapurple #{ $self } + #{ $self } & { background: rebeccapurple; }

What that selector compiles to is .grid + .grid .grid__item. The combination of $self and & enables us to generate that within the context of the element which is our &. This is incredibly powerful for maintaining some sort of order within complex codebases.

Wrapping up

We’ve taken inspiration from JavaScript to control scope in our BEM components with Sass by using this little snippet at the root: $self: &. With the scope control, we gain flexibility to write clean, organized, BEM driven CSS when we are deep in a modifier or child element.

I hope this little tip will help you to improve your Sass. If it does, get in touch with me on Twitter or drop a comment below. It’ll be great to see how gaining control of scope with Sass has helped you out.

Using Sass to Control Scope With BEM Naming is a post from CSS-Tricks

Everything you need to know about CSS Variables

This is by far the biggest deep dive I’ve seen on CSS Variables posted to the web and it’s merely Chapter One of complete e-book on the topic.

Truth is, I’m still on the thick of reading through this myself, but had to stop somewhere in the middle to write this up and share it because it’s just that gosh-darned useful. For example, the post goes into great detail on three specific use cases for CSS Variables and breaks the code down to give a better understanding of what it does, in true tutorial fashion.

Scoping, inheritance, resolving multiple declarations, little gotchas—there’s plenty in here for beginners and advanced developers alike.

OK, back to reading. 🤓

Direct Link to Article — Permalink

Everything you need to know about CSS Variables is a post from CSS-Tricks

Let’s Build a Custom Vue Router

Plenty of tutorials exist that do a great job in explaining how Vue’s official routing library, vue-router, can be integrated into an existing Vue application. vue-router does a fantastic job by providing us with the items needed to map an application’s components to different browser URL routes.

But, simple applications often don’t need a fully fledged routing library like vue-router. In this article, we’ll build a simple custom client-side router with Vue. By doing so, we’ll gather an understanding of what needs to be handled to construct client-side routing as well as where potential shortcomings can exist.

Though this article assumes basic knowledge in Vue.js; we’ll be explaining things thoroughly as we start to write code!


First and foremost: let’s define routing for those who may be new to the concept.

In web development, routing often refers to splitting an application’s props to dictate the information that should be displayed. Here’s a Pen that shows just this:

See the Pen Vue Pokemon by Hassan Dj (@itslit) on CodePen.

Though the app would functionally work, it misses a substantial feature that’s expected from most web applications—responding to browser navigation events. We’d want our Pokémon app to be accessible and to show different details for different pathnames: /charizard, /blastoise, and /venusaur. This would allow users to refresh different pages and keep their location in the app, bookmark the URLs to come back to later, and potentially share the URL with others. These are some of the main benefits of creating routes within an application.

Now that we have an idea of what we’ll be working on, let’s start building!

Preparing the App

The easiest way to follow along step-by-step (if you wish to do so) is to clone the GitHub repo I’ve set up.

Download on GitHub

When cloned, install the project dependencies with:

npm install

Let’s take a brief look within the project directory.

$ ls

There also exists the hidden files, .babelrc and .gitignore within the project scaffold.

This project is a simple webpack-configured application scaffolded with vue-cli, the Vue command line interface.

index.html is where we declare the DOM element—#app— with which we’ll use to mount our Vue application:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.3/css/bulma.css"> <link rel="stylesheet" href="../public/styles.css" /> <title>Pokémon - Routing</title> </head> <body> <div id="app"></div> <script src="/dist/build.js"></script> </body>

In the <head> tag of the index.html file, we introduce Bulma as our application’s CSS framework and our own styles.css file that lives in the public/ folder.

Since our focus is on the usage of Vue.js, the application already has all the custom CSS laid out.

The src/ folder is where we’ll be working directly from:

$ ls src/

src/main.js represents the starting point of our Vue application. It’s where our Vue instance is instantiated, where we declare the parent component that is to be rendered, and the DOM element #app with which our app is to be mounted to:

import Vue from 'vue';
import App from './app/app'; new Vue({ el: '#app', render: h => h(App)

We’re specifying the App component, from the src/app/app.js file, to be the main parent component of our application.

In the src/app directory, there exists two other files – app-custom.js and app-vue-router.js:

$ ls src/app/

app-custom.js denotes the completed implementation of the application with a custom Vue router (i.e. what we’ll be building in this article). app-vue-router.js is a completed routing implementation using the vue-router library.

For the entire article, we’ll only be introducing code to the src/app/app.js file. With that said, let’s take a look at the starting code within src/app/app.js:

const CharizardCard = { name: 'charizard-card', template: ` <div class="card card--charizard has-text-weight-bold has-text-white"> <div class="card-image"> <div class="card-image-container"> <img src="../../static/charizard.png"/> </div> </div> <div class="card-content has-text-centered"> <div class="main"> <div class="title has-text-white">Charizard</div> <div class="hp">hp 78</div> </div> <div class="stats columns is-mobile"> <div class="column">&#x1f525;<br> <span class="tag is-warning">Type</span> </div> <div class="column center-column">199 lbs<br> <span class="tag is-warning">Weight</span> </div> <div class="column">1.7 m <br> <span class="tag is-warning">Height</span> </div> </div> </div> </div> `
}; const App = { name: 'App', template: ` <div class="container"> <div class="pokemon"> <pokemon-card></pokemon-card> </div> </div> `, components: { 'pokemon-card': CharizardCard }
}; export default App;

Currently, two components exist: CharizardCard and App. The CharizardCard component is a simple template that displays details of the Charizard Pokémon. The App component declares the CharizardCard component in its components property and renders it as <pokemon-card></pokemon-card> within its template.

We currently only have static content with which we’ll be able to see if we run our application:

npm run dev

And launch localhost:8080:

To get things started, let’s introduce two new components: BlastoiseCard and VenusaurCard that contains details of the Blastoise and Venusaur Pokémon respectively. We can lay out these components right after CharizardCard:

const CharizardCard = { // ... }; const BlastoiseCard = { name: 'blastoise-card', template: ` <div class="card card--blastoise has-text-weight-bold has-text-white"> <div class="card-image"> <div class="card-image-container"> <img src="../../static/blastoise.png"/> </div> </div> <div class="card-content has-text-centered"> <div class="main"> <div class="title has-text-white">Blastoise</div> <div class="hp">hp 79</div> </div> <div class="stats columns is-mobile"> <div class="column">&#x1f4a7;<br> <span class="tag is-light">Type</span> </div> <div class="column center-column">223 lbs<br> <span class="tag is-light">Weight</span> </div> <div class="column">1.6 m<br> <span class="tag is-light">Height</span> </div> </div> </div> </div> `
}; const VenusaurCard = { name: 'venusaur-card', template: ` <div class="card card--venusaur has-text-weight-bold has-text-white"> <div class="card-image"> <div class="card-image-container"> <img src="../../static/venusaur.png"/> </div> </div> <div class="card-content has-text-centered"> <div class="main"> <div class="title has-text-white">Venusaur</div> <div class="hp hp-venusaur">hp 80</div> </div> <div class="stats columns is-mobile"> <div class="column">&#x1f343;<br> <span class="tag is-danger">Type</span> </div> <div class="column center-column">220 lbs<br> <span class="tag is-danger">Weight</span> </div> <div class="column">2.0 m<br> <span class="tag is-danger">Height</span> </div> </div> </div> </div> `
}; const App = { // ... }; export default App;

With our application components established, we can now begin to think how we’ll create routing between these components.


To establish routing, we’ll start by buiding a new component that holds the responsibility to render a specified component based on the app’s location. We’ll create this component in a constant variable named View.

Before we create this component, let’s see how we might use it. In the template of the App component, we’ll remove the declaration of <pokemon-card> and instead render the upcoming router-view component. In the components property; we’ll register the View component constant as <router-view> to be declared in the template.

const App = { name: 'App', template: ` <div class="container"> <div class="pokemon"> <router-view></router-view> </div> </div> `, components: { 'router-view': View }
}; export default App;

The router-view component will match the correct Pokémon component based on the URL route. This matching will be dictated in a routes array that we’ll create. We’ll create this array right above the App component:

const CharizardCard = { // ... };
const BlastoiseCard = { // ... };
const VenusaurCard = { // ... }; const routes = [ {path: '/', component: CharizardCard}, {path: '/charizard', component: CharizardCard}, {path: '/blastoise', component: BlastoiseCard}, {path: '/venusaur', component: VenusaurCard}
]; const App = { // ... }; export default App;

We’ve set each Pokémon path to their own respective component (e.g. /blastoise will render the BlastoiseCard component). We’ve also set the root path / to the CharizardCard component.

Let’s now begin to create our router-view component.

The router-view component will essentially be a mounting point to dynamically switch between components. One way we can do this in Vue is by using the reserved <component> element to establish Dynamic Components.

Let’s create a starting point for router-view to get an understanding of how this works. As mentioned earlier; we’ll create router-view within a constant variable named View. So with that said, let’s set up View right after our routes declaration:

const CharizardCard = { // ... };
const BlastoiseCard = { // ... };
const VenusaurCard = { // ... }; const routes = [ // ...
]; const View = { name: 'router-view', template: `<component :is="currentView"></component>`, data() { return { currentView: CharizardCard } }
}; const App = {
// ... }; export default App;

The reserved <component> element will render whatever component the is attribute is bound to. Above, we’ve attached the is attribute to a currentView data property that simply maps to the CharizardCard component. As of now, our application resembles the starting point by displaying CharizardCard regardless of what the URL route is.

Though router-view is now appropriately rendered within App, it’s not currently dynamic. We need router-view to display the correct component based on the URL pathname upon page load. To do this, we’ll use the created() hook to filter the routes array and return the component that has a path that matches the URL path. This would make View look something like this:

const View = { name: 'router-view', template: `<component :is="currentView"></component>`, data() { return { currentView: {} } }, created() { this.currentView = routes.find( route => route.path === window.location.pathname ).component; }

In the data function, we’re now instantiating currentView with an empty object. In the created() hook, we’re using JavaScript’s native find() method to return the first object from routes that matches route.path === window.location.pathname. We can then get the component with object.component (where object is the returned object from find()).

Inside a browser environment, window.location is a special object containing the properties of the browser’s current location. We grab the pathname from this object which is the path of the URL.

At this stage; we’ll be able to see the different Pokémon Card components based on the state of our browser URL!

The BlastoiseCard component now renders at the /blastoise route.

There’s something else we should consider. If a random URL pathname is entered, our app will currently error and present nothing to the view.

To avoid this, let’s introduce a simple check to display a “Not Found” template if the URL pathnamedoesn’t match any path existing in the routes array. We’ll separate out the find() method to a component method named getRouteObject() to avoid repetition. This updates the View object to:

const View = { name: 'router-view', template: `<component :is="currentView"></component>`, data() { return { currentView: {} } }, created() { if (this.getRouteObject() === undefined) { this.currentView = { template: ` <h3 class="subtitle has-text-white"> Not Found :(. Pick a Pokémon from the list below! </h3> ` }; } else { this.currentView = this.getRouteObject().component; } }, methods: { getRouteObject() { return routes.find( route => route.path === window.location.pathname ); } }

If the getRouteObject() method returns undefined, we display a “Not Found” template. If getRouteObject()returns an object from routes, we bind currentView to the component of that object. Now if a random URL is entered, the user will be notified:

The “Not Found” view is rendered if the URL pathname does not match any of the values in the routes array.

The “Not Found” template tells the user to pick a Pokémon from a list. This list will be the links we’ll create to allow the user to navigate to different URL routes.

Awesome! Our app is now responding to some external state, the location of the browser. router-view determines which component should be displayed based on the app’s location. Now, we need to construct links that will change the location of the browser without making a web request. With the location updated, we want to re-render our Vue app and rely on router-view to appropriately determine which component to render.

We’ll label these links as router-link components.


In web interfaces, we use HTML <a> tags to create links. What we want here is a special type of <a> tag. When the user clicks on this tag, we’ll want the browser to skip its default routine of making a web request to fetch the next page. Instead, we just want to manually update the browser’s location.

Let’s compose a router-link component that produces an <a> tag with a special click binding. When the user clicks on the router-link component, we’ll use the browser’s history API to update the browser’s location.

Just like we did with router-view, let’s see how we’ll use this component before we build it.

In the template of the App component, let’s create three <router-link> elements within a parent <div class="pokemon-links"></div> element. Rather than using the href attribute in <router-link>, we’ll specify the desired location of the link using a to attribute. We’ll also register the upcoming router-link component (from a Link constant variable) in the App components property:

const App = { name: 'App', template: ` <div class="container"> <div class="pokemon"> <router-view></router-view> <div class="pokemon-links has-text-centered"> <router-link to="/charizard"></router-link> <router-link to="/blastoise"></router-link> <router-link to="/venusaur"></router-link> </div> </div> </div> `, components: { 'router-view': View, 'router-link': Link }

We’ll create the Link object that represents router-link right above the App component. We’ve established the router-link component should always be given a to attribute (i.e. prop) that has a value of the target location. We can enforce this prop validation requirement like so:

const CharizardCard = { // ... };
const BlastoiseCard = { // ... };
const VenusaurCard = { // ... }; const routes = [ // ... ]; const View = { // ... }; const Link = { name: 'router-link', props: { to: { type: String, required: true } }
}; const App = { // ... }; export default App;

We can create the template of router-link to consist of an <a> tag with an @click handler attribute. Upon trigger, the @click handler will call a component method, labeled navigate(), that navigates the browser to the desired location. This navigation will occur with the use of the history.pushState() method. With that said, the Link constant object will be updated to:

const Link = { name: 'router-link', props: { to: { type: String, required: true } }, template: `<a @click="navigate" :href="to">{{ to }}</a>`, methods: { navigate(evt) { evt.preventDefault(); window.history.pushState(null, null, this.to); } }

Within the <a> tag, we’ve bound the value of the to prop to the element text content with {{ to }}.

When navigate() is triggered, it first calls preventDefault() on the event object to prevent the browser from making a web request for the new location. The history.pushState() method is then called to direct the user to the desired route location. history.pushState() takes three arguments:

  • a state object to pass serialized state information
  • a title
  • the target URL

In our case, there is no state information that’s needed to be passed, so we’ve left the first argument as null. Some browsers (e.g. Firefox) currently ignore the second parameter, title, hence we’ve left that as null as well.

The target location, the to prop, is passed in to the third and last parameter. Since the to prop contains the target location in a relative state, it will be resolved relative to the current URL. In our case, /blastoise will resolve to http://localhost:8080/blastoise.

If we click any of the links now, we’ll notice our browser updates to the correct location without a full page reload. However, our app will not update and render the correct component.

This unexpected behaviour happens because when router-link is updating the location of the browser, our Vue app is not alerted of the change. We’ll need to trigger our app (or simply just the router-view component) to re-render whenever the location changes.

Though there’s a few ways to accomplish this behaviour, we’ll do this by using a custom EventBus. An EventBus is a Vue instance responsible in allowing isolated components to subscribe and publish custom events between each other.

At the beginning of the file, we’ll import the vue library and create an EventBus with a new Vue() instance:

import Vue from 'vue'; const EventBus = new Vue();

When a link has been clicked, we need to notify the necessary part of the application (i.e. router-view) that the user is navigating to a particular route. The first step is to create an event emitter using the EventBus‘s events interface in the navigate() method of router-link. We’ll give this custom event a name of navigate:

const Link = { // ..., methods: { navigate(evt) { evt.preventDefault(); window.history.pushState(null, null, this.to); EventBus.$emit('navigate'); } }

We can now set the event listener/trigger in the created() hook of router-view. By setting the custom event listener outside of the if/else statement, the created() hook of View will be updated to:

const View = { // ..., created() { if (this.getRouteObject() === undefined) { this.currentView = { template: ` <h3 class="subtitle has-text-white"> Not Found :(. Pick a Pokémon from the list below! </h3> ` }; } else { this.currentView = this.getRouteObject().component; } // Event listener for link navigation EventBus.$on('navigate', () => { this.currentView = this.getRouteObject().component; }); }, // ...

When the browser’s location changes by clicking a <router-link> element, this listening function will be invoked, re-rendering router-view to match against the latest URL!

Great! Our app now navigates appropriately as we click each of the links.

There’s one last thing we need to consider. If we try to use the browser back/forward buttons to navigate through the browser history, our application will not currently re-render correctly. Although unexpected, this occurs because no event notifier is emitted when the user clicks browser back or browser forward.

To make this work, we’ll use the onpopstate event handler.

The onpopstate event is fired each time the active history entry changes. A history change is invoked by clicking the browser back or browser forward buttons, or calling history.back() or history.forward() programmatically.

Right after our EventBus creation, let’s set up the onpopstate event listener to emit the navigate event when a history change is invoked:

window.addEventListener('popstate', () => { EventBus.$emit('navigate'); });

Our application will now respond appropriately even when the browser navigation buttons are used!

And there we have it! We’ve just built a custom Vue router using an EventBus and dynamic components. Even with the tiny size of our app we can enjoy a noticeable performance improvement. Avoiding a full page load also saves hundreds of milliseconds and prevents our app from “blinking” during the page change.


I love Vue. One reason as to why – it’s incredibly easy to use and manipulate Vue components just like we saw in this article.

In the introduction, we mentioned how Vue provides the vue-router library as the official routing library of the framework. We’ve just created simple versions of the same main items that are used in vue-router:

  • routes: the array responsible in mapping components to respective URL pathnames.
  • router-view: the component that renders a specified app component based on the app’s location
  • router-link: the component that allows the user to change the location of the browser without making a web request.

For very simple applications, the routing we’ve built (or a variation thereof like this one built by Chris Fritz) can do the minimal amount of work needed to route our applications.

The vue-router library, on the other hand, is built in a more complicated manner and introduces incredibly useful capabilities, often needed in larger applications like:

  • Consistency between different browsers
  • Nested Routes
  • Navigation Guards
  • Transition Effects

Though the vue-router library does come with additional boilerplate, it’s fairly easy to integrate once your application is composed of well isolated and distinct components. If you’re interested, you can see the components of vue-router being used to enable routing in this application here.

Hopefully this was as enjoyable to you as it was for me in compiling this post! Thanks for reading!

This article is an adapted (and summarized) segment from the upcoming book, Fullstack Vue, that I’m working on with the Fullstack.io team! Having the opportunity to work with the folks at Fullstack has been nothing short of being a blast. In true Fullstack fashion, the book covers numerous facets of Vue including but not restricted to routing, simple state management, form handling, Vuex, server persistence, and testing. If this is something that piques your interest or if you have any questions at all, follow (or message) me on twitter (@djirdehh)! If the above doesn’t pique your interest, you can still follow me anyway. 😛

Let’s Build a Custom Vue Router is a post from CSS-Tricks

AMP News

AMP is controversial, to say the least. Dave and I did a show on it about a year ago that to me felt fairly balanced in addressing some of the issues. Let’s cover some recent news and responses.

One thing that isn’t usually controversial: it’s fast. AMP pages are damn performant. Even that, though, is contentious. Ferdy Christant notes:

Technically correct AMP pages will perform very similar to any non-horrible web page.

The difference between AMP performing instantly and getting numbers ranging from 2–8s as seen above have to be explained.

Part of that answer you can probably guess: the cache is simply very fast. It’s hard to compete with a Google-class CDN.

You don’t need AMP to have a fast website.

The most controversial bit is that carrot offered for using AMP: the search results news carousel. The carousel is extremely prominent in Google search results, and AMP is a one-way ticket to get in. You could make a site faster than AMP, but that doesn’t meet the criteria for entry. Tim Kadlec:

there has been no indication of any attempt to address the first issue, that of incentivization and premium placement. In fact, not only has there been no attempt to fix it, it appears the AMP team is doubling-down on those incentives instead.

Doubling-down, as in, AMP stories will be released soon and will also enjoy premium placement on Google. Every indication is that the primary desire of people reaching for AMP is the preferential search results treatment. Gina Trapani:

In my experience people are motivated to use AMP… I’ve seen this from our clients…mostly because of SEO. They want it in that top stories carousel, they want that lightning bolt of approval in regular search results which is happening now.

Of course, Google can do whatever they want. They are an independent company and if they wanna tell us that we have to use a special format to have benefits on their platform, then that’s the way it is. It doesn’t mean we have to be happy about it. Hundreds of people have signed the AMP letter, which calls for two changes:

  1. Instead of granting premium placement in search results only to AMP, provide the same perks to all pages that meet an objective, neutral performance criterion such as Speed Index. Publishers can then use any technical solution of their choice.
  2. Do not display third-party content within a Google page unless it is clear to the user that they are looking at a Google product. It is perfectly acceptable for Google to launch a “news reader” but it is not acceptable to display a page that carries only third-party branding on what is actually a Google URL, nor to require that third party to use Google’s hosting in order to appear in search results.

Ethan Marcotte is concerned:

absent action from some sort of regulatory body, I’m not sure what influence you or I could exert to change the trajectory of AMP

…but thinks we could perhaps collectively have influence. Jeremy Keith has called some of the messaging behind AMP an outright lie:

I don’t think the developers working on the AMP format are intentionally deceptive (although they are engaging in some impressive cognitive gymnastics). The AMP ecosystem, on the other hand, that’s another story—the preferential treatment of Google-hosted AMP pages in the carousel and in search results; that’s messed up.

Jeremy also notes that the power Google is exerting here is worrisome. Part of the stated motivation is trying to fix the web. Taking a stand, as it were.

I remember feeling very heartened to see WikiPedia, Google and others take a stand on January 18th, 2012 (against SOPA and PIPA). But I also remember feeling uneasy. In this particular case, companies were lobbying for a cause I agreed with. But what if they were lobbying for a cause I didn’t agree with? Large corporations using their power to influence politics seems like a very bad idea. Isn’t it still a bad idea, even if I happen to agree with the cause?

Cloudflare quite rightly kicked The Daily Stormer off their roster of customers. Then the CEO of Cloudflare quite rightly wrote this in a company-wide memo:

Literally, I woke up in a bad mood and decided someone shouldn’t be allowed on the Internet. No one should have that power.

There’s an uncomfortable tension here.

AMP is also expanding to other technology, notably email. Well, Gmail, that is. Fast, well-rendering, interactive emails seem like a hugely positive thing. Perhaps predictably at this point, people in that industry have similar concerns. Jason Rodriguez:

I’m an email guy. I’ve written three books on email, spoken at a bunch of conferences on the topic, and help build tools for other email folks at my day job. I love seeing the email platform grow and evolve. I love seeing people working on interesting ideas that make email more valuable for the subscribers that receive them.

So, you’d think I’d be thrilled by Google’s announcement about adding dynamic content and interactivity to Gmail with AMP for Email. You’d be wrong.

Jason’s primary concern being that instead of improving support for what we already have, they’ve invented a new format and called it open-sourced, but have full control over it. However, with far more blockers in the way (e.g. ESPs not supporting the new MIME type) and less carrots to offer, it seems like a long shot it will happen.

I know I’ve covered a lot of negative news here, but that’s mostly what I’ve been seeing. Strangely enough, I feel more interested in watching how this all shakes out than I am motivated to weigh in on either side.

AMP News is a post from CSS-Tricks

Working Towards Better Naming

There is a quote that I bet every one of you has heard many times. You know the one. The one that reminds you how hard naming is.

Let’s talk names.

We talk often about how hard naming is, but it’s less common see people talk about how to get better at it. Even naming philosophies lend structure to naming, but don’t pick names for you. Names are more than just some hard thing we get needlessly caught up in. They’re essential to good code. Good names can make a code base easy to pick up and work with. Bad names are hard to understand, or worse yet, prone to error.

Naming Examples

Let’s look at some examples in JavaScript.

function processData(data) { var result = JSON.parse(data); return result;

Reading only the function name, parameter name, and returned variable, we see that processData gets data and returns a result. These names have added nearly zero information to the reader. Code like this is easy to write when you just want to get it working or you’re trying to stay flexible to changes, and that’s okay. It’s always a good idea to go over your code with fresh eyes to fix things, and names should be on your quality checklist.

Here’s a more descriptive way we could have written the last example:

function parseJson(string) { var jsonObject = JSON.parse(string); return jsonObject;

Technology is one of the most abbreviation and acronym heavy fields there are, and they are key to great names. Working Towards Better Naming is a post from CSS-Tricks


Frank Chimero published a new talk-turned-essay, Everything Easy is Hard Again.

May we all be as wonderfully self-reflective and eloquent as Frank one day. There is a lot there, so please read it. Part of the theme is that web design and development has seemingly repetitive cycles that can kick even quite experienced people back down the ladder:

I don’t feel much better at making [websites] after 20 years. My knowledge and skills develop a bit, then things change, and half of what I know becomes dead weight. This hardly happens with any of the other work I do.

And complexity is to blame.

Complexity is one of those in the water topics right now. Kevin Ball wrote “we are in the midst of a massive and rapid period shift in complexity from the backend to the front.” Roneesh just celebrated untangling all this front end complexity in his own mind and shared his thoughts.

I’ve read a good number of responses to Frank’s article as well. I particularly liked our own Robin Rendle’s from the newsletter:

I believe the reason for all this complexity and nuance in the work is because we no longer expect the web to be a series of simple, hyperlinked documents.

Our expectations, as users of the web, have now completely changed.

Websites are machines in and of themselves; they have state, notifications, alerts and warnings, components that appear under certain circumstances, location-aware features and complexity beyond anything we were building fifteen years ago. There are more devices and viewport widths rendering our websites with increasingly difficult performance requirements.

That feels right to me. It’s just what I was feeling when I wrote What is the Future of Front End Web Development?

What websites are being asked to do is rising. Developers are being asked to build very complicated things very quickly and have them work very well and very fast.

Though, Frank points out that even layout, fonts, and images have ballooned in complexity. The cause there I’d argue is the rightful focus (and really, demand for) performance. But reactions to complexity are usually those things plus a dozen or two other things.

Perhaps things didn’t get complicated for no reason, and instead got complicated to compete. The web can do more, so we ask it to do more.

It’s tempting to think that the complexity comes entirely from that moreness. Embracing more of the potential of the web, playing catchup with native apps, and building more powerful tools for people. But I’m sure the whole story is more (ahem) complicated. Someone once told me the reason they think developer tooling has evolved and gotten more complicated is that developers generally aren’t asked to innovate on the business and product side. They build what they are told to, so they use their smarts to innovate on their own tools.

It depends though. A few personal examples.

I’ve been running CSS-Tricks for over a decade. It’s a garden variety WordPress site, and while it’s certainly evolved, it’s not all that much more complicated today as it was in the first few years. I’ve gotten better at working on it, in part because it’s changed so little that I’m more comfortable doing that work. I know just what all the spinning gears do and just where to put the oil, most of the time.

On the other hand, through CodePen, I’ve experienced a long product development which started fairly simple and has come to extreme complexity. We sometimes question if we’ve overdone the complexity, but for the most part, each step in that direction has been a response to make something else, ironically enough, less complicated. One example of that was the adding of React’n’Redux’n’Friends, which was a step up in the complexity of the development workflow, build, and deploy processes but, believe it or not, was a step down in a complexity of the codebase. These tools help us build faster, debug easier, stay well tested, and provide a performant experience, to name some of the benefits. We didn’t add tooling just for kicks; we added tooling because we had growing problems to address.

Not everyone has the same problems. The web is a big place is a phrase I see thrown around sometimes, and I like it. Over a billion websites, they say. A big place indeed.

Check out Dan Cederholm’s favorite website:

It’s not responsive. It’s not optimized for iPhone. It looks blurry on a Retina display. It doesn’t use the latest HTML5/CSS3 framework. It doesn’t have a thoughtful vertical rhythm. The fonts are nothing special. It is neither skeuomorphic nor flat. It doesn’t have its own favicon. It doesn’t have a native app or Twitter or Instagram. It doesn’t use AJAX or SCRUM or node.js or Sinatra. It doesn’t have an API or an RSS feed or VC funding. It hasn’t been featured on a prominent tech blog or won an award.

It tells me the soups of the day.

I suspect it doesn’t need any more complexity, and literally nobody is advocating it does. That’s why that the web is a big place sentiment is so useful. We talk about complexity, but it’s all opt-in. A wonderfully useful (and simple) website of a decade ago remains wonderfully useful and simple. Fortunately for all involved, the web, thus far, has taken compatibility quite seriously. Old websites don’t just break.

I’ll bet the bits in Frank’s essay about web layout will strike a chord to many readers of this site. Frank makes the connection between table layout and grid layout. It’s not a rare sentiment. For example:

I’m sure Frank understands the benefits of the new grid layout (e.g. try re-arranging a <table> at a media query breakpoint), but the sentiment was more about cycles than a deep technical dive.

I’d say a reasonable argument could be made that, with CSS in particular, things are easier these days. CSS of old had us biting our fingernails about cross-browser support, scattering vendor prefixes throughout our code, and (lol) saying a prayer to the box model. Eric Meyer, despite publishing a heavy tome of CSS knowledge lately, says:

CSS has a great deal more capabilities than ever before, it’s true. In the sense of “how much there potentially is to know”, yes, CSS is more of a challenge.

But the core principles and mechanisms are no more complicated than they were a decade or even two decades ago. If anything, they’re easier to grasp now, because we don’t have to clutter our minds with float behaviors or inline layout just to try to lay out a page.

Swinging it back to developers innovating on their own tools for a moment, another consideration is the impact of site builders on our industry. I’ll always recommend a site builder app in the right circumstances. Does your photography business need a portfolio site? Your bakery a homepage? Your custom scarf site a blog and eCommerce site? Assuming this is a sub-$10,000 job, I’d rather see you use a specialized site builder product than hire out the job to anyone who is going to build something entirely custom. I don’t wanna go too far down that rabbit hole, but suffice it to say, because I’m not alone in that thinking, the market for low-end custom design and development work is rather gone.

There are more developers these days working on in-house teams or agencies with big-ticket clients. That is, more and more developers on large-scope, long-scale, highly-complex jobs. So that’s where their minds are at. Big complicated problems with big complicated solutions. That’s what gets talked about. That’s what gets blogged about. That’s what gets argued about. That’s the topic at a lot of conferences I’ve been to.

While you certainly can make a soup-of-the-day website with an index.html file and FTP, blog posts about that are fewer and farther between and don’t get as many claps.

Shout out to Dave Rupert, my friend and ShopTalk Show co-host, who’s been pushing back against complexity in all its forms for as long as I’ve known him. I’m still trying to catch up.

Complexity is a post from CSS-Tricks

Understanding Web Fonts and Getting the Most Out of Them

Thierry Blancpain is a brand and interaction designer at Informal Inquiry in New York City and co-founder of Grilli Type, a Swiss type foundry. While this article is generally applicable to all web fonts, Grilli Type fonts are used throughout as examples of the concepts, particularly those demonstrating OpenType features.

Using your own fonts instead of system fonts is getting easier, but it’s still an evolving field. We’ll go over the different types of font formats and cover tips and best practices for them in this post. We’ll also dive into more in-depth features for those of you who want to level up and aim to perfect the craft with advanced concepts and considerations when using web fonts. In the end, you’ll hopefully feel equipped not only to put web fonts to use but to get the most out of them.

Here we go!

Font Formats

When you purchase web fonts licensing, you receive a package of font files that typically include at least some of the following formats:

  • Embedded OpenType (EOT): @font-face to include fonts in CSS.

    Here’s the deepest level of support, including all of the font file formats we’ve discussed so far:

    @font-face { font-family: FontName; src: url('path/filename.eot'); src: url('path/filename.eot?#iefix') format('embedded-opentype'), url('path/filename.woff2') format('woff2'), url('path/filename.woff') format('woff'), url('path/filename.ttf') format('truetype');

    We can trim things down quite a bit if we’re only aiming to support modern browsers:

    @font-face { font-family: FontName; src: url('path/filename.woff2') format('woff2'), url('path/filename.woff') format('woff');

    Once the font has been declared and defined, we can put it to use on our elements. For example:

    body { font-family: 'FontName', Helvetica, Arial, sans-serif;

    Hosting Web Fonts

    One of the most flexible ways to load web fonts is to self-host them. That means that you host the files on your own server and your fonts will always be available when a visitor comes to your website without a third-party dependency. Neither tracking codes nor JavaScript is generally required to load self-hosted font files. Many small type foundries offer fonts as a direct download so they can be self-hosted and at Grilli Type, we are convinced it’s the best way to serve fonts.

    While some type foundries offer self-hosting (both with and without cumbersome restrictions and requirements), others only offer hosted solutions, meaning they host the files on your behalf. Some of the well-known ones include Hoefler & Co., Font Bureau, and Typotheque. Font Bureau and Typotheque offer their fonts in both ways at different price points.

    Make sure you know how a type foundry’s web fonts are offered before you buy licensing, because the difference in hosting and the terms of use can affect how they are implemented. Get what makes the most sense for you and fits your needs, and make sure you’re using them legally and according to the foundry’s licensing agreement.

    Advanced Typographic Features

    Let’s take a look at some of the more advanced features of web fonts.

    Spacing and Kerning

    There are two settings inside font files that define the space between characters:

    1. letter-spacing: This is defined as side bearings on the left and right side of each character
    2. font-kerning: This refers to specific adjustments between two characters

    Spacing cannot be turned off at all, because otherwise the text rendering engine (your browser) wouldn’t know what to do with these letters. Kerning, on the other hand, is turned off by default in browsers and has to be turned on by you in your CSS.

    Comparing type with kerning enabled and disabled.

    It’s easier to control kerning than you might think! Here’s how to activate it across all browsers that support it:

    p { font-feature-settings: "kern" 1; font-kerning: normal;

    If you don’t use a something like Autoprefixer to help manage browser support in CSS, then you’ll want to manually write out the browser vendor prefixes for this setting to extend browser support to older versions:

    /* All vendor prefixes for `font-feature-settings` */
    p { -moz-font-feature-settings: "kern" 1; -ms-font-feature-settings: "kern" 1; -o-font-feature-settings: "kern" 1; -webkit-font-feature-settings: "kern" 1; font-feature-settings: "kern" 1; font-kerning: normal;

    Advanced OpenType Features

    We just discussed how to use the font-feature-settings attribute to control kerning, but it can also be used to control other available OpenType feature in your web fonts. The number of supported features has been growing over time and the CSS-Tricks almanac is a good place to reference what is possible with OpenType.

    OpenType features are really exciting because they open up a bunch of possibilities for controlling fonts without having to serve multiple files to get the same effect. At the same time, it’s worth noting that the features an OpenType font file supports is up to the font designer and that not all fonts support the same features.

    To illustrate how advanced OpenType features can be chained together, the following code would turn on the numeric characters of an OpenType-enabled font that supports both old-style numerals (onum) and proportional numerals (pnum), plus enable kerning and activate a specific stylistic set included in the font:

    .my-element { font-feature-settings: "onum" 1, "pnum" 1, "kern" 1, "ss01" 1;
    Type with and without the activated OpenType features in the code example.

    The font-feature-settings attribute can be used to activate stylistic alternates, discretionary ligatures, different types of figures available in a font, small caps, and other handy things. Typofonderie has a nice overview of these advanced features, including examples.

    Because font-feature-settings is used to set many OpenType features at once, it’s not possible to define a single setting differently as the other choices will not be inherited. All of the features would need to be defined again to change the settings for child elements.

    Letting Spacing and Word Spacing

    CSS has long supported the letter-spacing and word-spacing attributes. When used correctly, both provide a fair amount of control over two very important aspects of how your type will look.

    As with all things typography, you’ll want to learn how to evaluate different options both functionally and visually and make decisions based on your impression. Different contexts may call for different spacing needs.

    At smaller sizes, most typefaces will benefit from a little extra spacing between characters and words. In larger contexts, like headings, typefaces may benefit from more narrow spacing. In either case, the right decisions require attention and your best judgment based on the outcomes.

    I’ve found that letter-spacing and word-spacing both work best using em units for the values. That allows the spacing to adjust fluidly based on the font size of the element they are applied to. The following example will give your content a little more room to breathe at smaller font sizes:

    p { font-size: 12px; letter-spacing: 0.015em; word-spacing: 0.001em;
    Comparing the difference with letter and word spacing turned on and off.

    Font Rendering

    Using type on screens brings up important questions about how they are rendered. Fonts are usually designed on about a 1000 units tall grid—or even larger—but then are displayed at something like a 16px font size. In an interplay between device, screen, and software, this reduction in resolution and fidelity requires some smarts to make small type legible and good-looking. Again, be observant, test in many browsers, and use your best judgment to put the best methods to use to increase legibility.


    Every operating system treats fonts differently from one another. In MacOS, the smarts are in the operating system (and thus can evolve over time), while the fonts themselves can be dumb. Historically, on Windows, the smarts were supposed to be included in the font software, and the system was supposed to use those smarts to decide how a font should be rendered at different sizes.

    Those smarts are called hinting. Hinting information embedded in the font files can tell a computer that the two stems of an “H” character are supposed to have the same line width, or that the space above and below the crossbar should stay about equal at smaller sizes.

    Hinting is a very complex and complicated topic, but the important takeaway is that the same font at the same size might render differently, even on the same computer depending on many factors, including the screen, the browser, and even the font and background color.

    Microsoft provides a guide on the topic of hinting. Even though it was initially released in 1997, it’s still a good read because it so thoroughly explores the topic.

    Font Smoothing

    While hinting information included in the font files is mostly being ignored on MacOS, specific browsers offer some additional control over font rendering.

    p { -webkit-font-smoothing: antialiased; /* Chrome, Safari */ -moz-osx-font-smoothing: grayscale; /* Firefox */

    Using these CSS properties leads to sharper, thinner text rendering on MacOS and iOS. But beware: this can also lead to rendering problems, especially if you’re already using a thin font or font weight.

    Both antialiased and grayscale are mainly useful to balance the rendering of fonts when using light text on dark backgrounds, as they would otherwise get rendered quite a bit bolder.

    The font-smoothing property and its values are not on the path to become a standard CSS feature, so use it with caution and perhaps only in contexts where you know you need to target a specific browser and context.

    Caution: OptimizeLegibility

    We often come across this attribute when troubleshooting font usage on Grilli Type customer websites:

    p { text-rendering: optimizeLegibility;

    Among other things, it activates kerning. That was very useful at some point, but is not needed anymore (as shown above). In addition to kerning, it also activates all kinds of ligatures, including extravagant ones that may be present in the font files.

    Although there are some use cases for this, do not use this feature if you don’t know exactly what you’re doing with it. Chance are you don’t need it in the first place.

    Web Font Resources

    If you’re ready to dive deeper into web fonts, here are a handful of recommended resources you can use to learn more:

    • Clagnut’s OpenType CSS Sandbox by Richard Rutter: A great place to test out OpenType features and easily put together your required CSS code.
    • Webfont Handbook by Bram Stein: This is the most in-depth e-book you can possibly read on web fonts, font rendering, and font performance.
    • Copy Paste Character: This is a great website that allows you to access pretty much any special character you might ever use.
    • Using @font-face by CSS-Tricks: This article includes snippets for declaring web fonts based on varying browser support.

    Advanced Web Font Considerations

    For those who are ready to level up to more advanced techniques, here are even more considerations to take into account:

    Uploading Licensed Fonts to Github

    If you commit a project to a public repo and use font files that you have licensed, please make sure that either the fonts or the directory that contains them is included in your .gitignore file so that they do not get uploaded. This will prevent others from taking and using your font files, and it can prevent you from breaking any terms of use for licensed fonts that usually have usage and sharing restrictions.

    .DS_Store path/to/web/fonts/folder/*

    Font Loading Tactics

    Loading web fonts can be as easy as simply using @font-face but that doesn’t necessarily offer the best possible performance. For example, it opens up the possibility of a Flash Of Unstyled Text (FOUT) which might be considered poor “A Comprehensive Guide to Font Loading Strategies” covers that and methods to improve the loading experience that will make you and your users very happy.

    Base64-Encoded Font Files

    In some rare instances, encoding your fonts as base64 inside your CSS will be a good idea but, generally, it is not—and, not to mention, you might break your font’s licensing agreement in the process. Be sure to proceed with a lot of caution and read up on your font’s terms of use when considering base64.

    CSS Text Decoration

    The W3C is working on a draft for new controls for text decoration, mainly dealing with how to make underlining text better and easier in CSS. This is not yet usable across all browsers, but have a look!

    Variable Fonts

    In 2017, the OpenType fonts specification 1.8.2 was released, allowing for what is called Variable Fonts. This new version of OpenType will allow for the inclusion of multiple font styles into a single font file, reducing server requests and web font file sizes. Depending on the type designer’s choices, it may also allow for the use of arbitrary weights in between existing weights and widths of fonts, among other things. Axis Praxis is a good website to play around with some existing test fonts – you will need a recent version of Safari or Chrome to do so, though.

    Wrapping Up

    We covered a lot in this article! Hopefully now you have a good understanding of the different font files out there, how to work with them, and all the amazing and powerful ways fonts can be styled using both tried and true methods and cutting-edge features.

    Understanding Web Fonts and Getting the Most Out of Them is a post from CSS-Tricks

My Talk Writing Process

Some people have a talk preparation process that is super organized and runs like a well-oiled machine. Mine, on the other hand, is a bit messy, but it works for me. Even when a talk looks polished and put together on stage, it doesn’t mean the process to get it there was that way too.

Me on stage at An Event Apart.

When putting together a new talk recently, I noticed there was most definitely a pattern to how my talks take shape. Here’s how the talk-making process goes for me:

The Research Phase

True to the nature of research, all my talks start by collecting articles, books, videos, and other things that relate to the topic of the talk. At this point in the talk development process, I usually only have a general topic idea instead of a fully fleshed out point-of-view or main message. That means I end up collecting things that might only be tangentially related to the topic and going down some strange topical rabbit holes.

I’ll save all these as a collection of bookmarks but usually also in a Google Doc with notes or quotes from the piece that seem most relevant. That makes it easier to keep a high-level view of what I’ve collected, and even discover interesting threads that connect some of the seemingly unrelated sources.

This initial phase is intentionally fuzzy on focus. Only a small percentage of the research I collect actually makes it to the end talk content. Sometimes the things that don’t make it spark ideas for other talks, or just gets filed away in my brain for future (hopefully interesting) things.

Outlining: The Giant Mess Phase

There’s probably a smarter sounding name for this phase, but for every talk I’ve done, there’s always a point where I step back and think, “Holy crap this is all a giant mess! What am I even doing with all this!?” So, that’s what I’ll call this phase. This is the phase where I make a general outline for the talk (again in a Google Doc or Word file) and start fitting my thoughts, examples or demos, and relevant research into it.

I outline the major points I think the talk should make and try to fit them into some sort of narrative order. These tend to change and morph a bit as the talk takes shape, but that’s OK because I’m still in the giant mess phase.

At the top of my document I have a three-part block that helps keep me focused:

What is the main question this talk answers (or the main problem it addresses)?

What is the main message of this talk?

Three points that support the main message:

(I started doing this on advice from Bill Smartt, and it’s been a huge help ever since.)

A talk outline for a 30 min talk complete with comments to myself.

The rest of the document addresses the body of the talk with each main point as a headline and some notes underneath it. Personally, I don’t write out talks word-for-word and memorize them. I do write out an introduction and conclusion to make sure I’m setting up the topic and summarizing it well, but the rest of the notes are bulleted lists of points to make and references supporting examples, demos, or references. I leave space at the bottom of the document for random thoughts and notes as well as probably a few too many comments to myself on possible changes to make or different directions to take. Points that don’t fit into the main narrative get moved down to this section too.

Once I feel like I have a cohesive outline, or at least one that isn’t a total mess, I move on to making some visuals.

The Editing Phase

A recent talk with all the edited-out slides shown ghosted out. Those slides never made it to the final talk.

This is the point where I start making slides and such based on the outline. Some people leave slides to the very last thing, but I leave them more to the almost last thing and do some of my thinking and organizing while I build up the slide deck.

I’ll make slides for each point in the outline, take screen recordings of demos or examples, and start piecing things together in order. I tend to think of my talks in sections at this point and, as I create the slides for each section, I’ll try talking through them out loud to see how they flow. (It’s amazing how different things sound when you say them out loud!)

There is a lot of rearranging and cutting out during this phase to work towards something that feels cohesive. I keep working on adding, deleting, and rearranging slides until I’ve got visuals for the full narrative of my outline. Sometimes things fall into place quickly, but for most talks this part can take a while.

At this point I almost always have far too much content. I’ll run through what I have for the talk in 10 to 15 minute chunks, editing down and solidifying points until I’ve got something that fits neatly into the required time length. Most times this means a 30 minutes and 45-60 minute version of the talk depending on the format of the event where it’s being given.

The same talk without the slides that have been edited out.

Rehearsing: The Talking to Myself Phase

Rehearsing is so important but it can also be very awkward. It seems like everyone has a different strategy for rehearsing talks, which totally makes sense. I have a really hard time rehearsing talks in their entirety when I’m standing in my office talking to the wall or to the dog, so I tend to rehearse in 15 or 30 minute chunks; practicing the first half then taking a break to do some other things and coming back to run through the second half. That way I know I have a handle on all the material, but haven’t driven myself (or the dog) up a wall with all that talking to no one in particular.

Ah, that familiar presenter notes view. I like a giant notes window even though I rarely actually read them while I’m on stage. They’re a “just in case” kind of thing.

As often as possible I’ll try to give a new talk to a few friends or at a meetup before doing it on stage for the first time. Having a real live human audience can really help show which points are strong and which might need a bit more work to get across well. I also always run through the “final” talk from start-to-finish at least once in the 24 hours before I’ll be on stage to make sure all the content is fresh in my mind.

A Talk is Never Really Done

Seriously. They really never are. The funny thing about talks is that when you give them more than once, they’re rarely exactly the same. (Yes, it is totally fine to do the same talk more than once.) There is always something to improve, something to add, or new points or examples to add to the narrative.

I usually make notes for myself on what worked or what didn’t right after getting off stage. That’s a good time to recall which parts of the talk felt like they could use some work, but it’s not such a good time to actually make any edits. I’ll go back to those notes a few days later (having some space here is really helpful) and make adjustments as needed. Also, if I come across other relevant examples or research at any point in time, I’ll try to add them into the talk for the next time around.

You Do You

If there’s one thing I’ve learned working on talks and talking to other speakers about their process, it’s that no two people work the same way. Everyone has their own way of putting together talks that they’ve customized for their own habits and preferences. If your talk development process looks nothing like mine, that’s totally fine. And if you haven’t found a process that works for you yet, keep experimenting with different techniques. You’ll find one that works for you!

For more on how to get a talk together, check out these other articles too:

  • How to Write a Talk
  • My Process: Writing a Talk
  • Three things you don’t need to become a speaker
  • How to write a successful talk proposal

My Talk Writing Process is a post from CSS-Tricks