Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

July 10 2020

14:54

An Eleventy Starter with Tailwind CSS and Alpine.js

When I decided to try to base my current personal website on Eleventy, I didn’t want to reinvent the wheel: I tested all the Eleventy starters built with Tailwind CSS that I could find in Starter Projects from the documentation.

Many of the starters seemed to integrate Tailwind CSS in a contrived way. Also, some of them seemed to assume that no one updates Tailwind’s configuration on the fly while working on a website. That’s why I integrated Eleventy with Tailwind CSS and Alpine.js myself. I have reason to believe that you’ll like the simplicity of my solution.

Good design is as little design as possible.

—Dieter Rams, 10 Principles for Good Design

If you’re uninterested in the details, feel free to grab my starter and jump right in.

Getting started

I’m going to assume you have a general understanding of Tailwind CSS, HTML, JavaScript, Nunjucks, the command line, and npm.

Let’s start by with a new a folder, then cd to it in the command line, and initialize it with a package.json file:

npm init -y

Now we can install Eleventy and Tailwind CSS. We’ll throw in PostCSS as well:

npm install --save-dev @11ty/eleventy tailwindcss postcss-cli

We need to create a page to test whether we’ve successfully set things up. In a real use case, our pages will use templates, so we’ll do that here as well. That’s where Nunjucks fits into the mix, serving as a templating engine.

Let’s make a new file called index.njk in the project folder. We’ll designate it as the homepage:

{% extends "_includes/default.njk" %}


{% block title %}It does work{% endblock %}


{% block content %}
  <div class="fixed inset-0 flex justify-center items-center">
    <div>
      <span class="text-change">Good design</span><br/>
      <span class="change">is<br/>as little design<br/>as possible</span>
    </div>
  </div>
{% endblock %}

Basic templating

Now let’s create a new folder in the project folder called _includes (and yes, the folder name matters). Inside this new folder, we’ll create a file called default.njk that we’ll use as the default template for our layout. We’ll keep things simple with a basic HTML boilerplate:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>
      {% block title %}Does it work?{% endblock %}
    </title>
    <meta charset="UTF-8"/>
    {% if description %}
      <meta name="description" content="{{description}}"/>
    {% endif %}
    <meta http-equiv="x-ua-compatible" content="ie=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
    <link rel="stylesheet" href="/style.css?v={% version %}"/>
    {% block head %}{% endblock %}
  </head>
  <body>
    {% block content %}
      {{ content | safe }}
    {% endblock %}
  </body>
</html>

Configuring Tailwind CSS

Let’s take care of a test for Tailwind CSS in as few moves as possible. First, create a new subfolder called styles and a file in it called tailwind.config.js:

module.exports = {
  theme: {
    colors: {
      change: "transparent"
    }
  },
  variants: {},
  plugins: [],
}

Then, create a file called tailwind.css in that same styles folder:

/* purgecss start ignore */
@tailwind base;
/* purgecss end ignore */


.change {
  color: transparent;
}


/* purgecss start ignore */
@tailwind components;
/* purgecss end ignore */


@tailwind utilities;

We’re done with the styles folder for now. What we do need is a configuration file that tells PostCSS to use Tailwind CSS, which we can get by creating a new file in the root directory of the project folder called postcss.config.js. Here’s how we require Tailwind CSS and its configuration file with PostCSS:

module.exports = {
  plugins: [
    require(`tailwindcss`)(`./styles/tailwind.config.js`)
  ],
};

Starting and building the project

Now let’s create another new file in the same root directory called .gitignore. This will allow us to define what files to skip when committing the project to a repo, like on GitHub:

_site/
_tmp/
.DS_Store
node_modules/
package-lock.json

Next, is another new file, this time one that tells Eleventy what it can ignore, called .eleventyignore. It only needs one line:

node_modules

OK, now we will create a file called .eleventy.js (note the leading dot!) that basically configures Eleventy, telling it what files to watch and where to save its work:

module.exports = function (eleventyConfig) {
  eleventyConfig.setUseGitIgnore(false);


  eleventyConfig.addWatchTarget("./_tmp/style.css");


  eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" });


  eleventyConfig.addShortcode("version", function () {
    return String(Date.now());
  });
};

We can now update the package.json file with all of the scripts we need to start and build the site during development. The dependencies should already be there from the initial setup.

{
  "scripts": {
    "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch",
    "build": "ELEVENTY_PRODUCTION=true eleventy & ELEVENTY_PRODUCTION=true postcss styles/tailwind.css --o _site/style.css"
  },
  "devDependencies": {
    "@11ty/eleventy": "^0.11.0",
    "postcss-cli": "^7.1.0",
    "tailwindcss": "^1.4.6"
  }
}

Hey, great job! We made it. Build the project to generate the initial CSS — this step is only required the very first time we set up. From the command line:

npm run build

And — drumroll, please — let’s officially start the site:

npm run start

Open the page http://localhost:8080 in your browser. It’s not gonna look like much, but check out the page title in the browser tab:

It does work!

We can still do a little more checking to make sure everything’s good. Open up /styles/tailwind.config.js and change the transparent color value to something else, say black. Tailwind’s configuration should reload, along with the page in your browser.

Don’t lose sight of your browser and edit /styles/tailwind.css by changing transparent to black again. Your CSS file should reload and refresh in your browser.

Now we can work nicely with Eleventy and Tailwind CSS!

Optimizing the output

At this point, Tailwind CSS works with Eleventy, but the CSS file is huge. The generated HTML isn’t perfect either because it contains stuff like redundant newline characters. Let’s clean it up:

npm install --save-dev @fullhuman/postcss-purgecss postcss-clean html-minifier

Open up postcss.config.js and replace what’s in there with this:

module.exports = {
  plugins: [
    require(`tailwindcss`)(`./styles/tailwind.config.js`),
    require(`autoprefixer`),
    ...(process.env.ELEVENTY_PRODUCTION
      ? [
          require(`postcss-clean`),
          require(`@fullhuman/postcss-purgecss`)({
            content: ["_site/**/*.html"],
            defaultExtractor: (content) =>
              content.match(/[\w-/:]+(?<!:)/g) || [],
            whitelist: [],
            whitelistPatterns: [/body/, /headroom/, /ril/],
          }),
        ]
      : []),
  ],
};

In the future, in the process of creating your website, if something looks wrong after you build the project and you have the impression that fragments of CSS are missing, add “ignored” class names to the whitelist in the file above.

Add the following line to the beginning of the .eleventy.js file:

const htmlmin = require("html-minifier");

We also need to configure htmlmin in .eleventy.js as well:

eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
    if (
      process.env.ELEVENTY_PRODUCTION &&
      outputPath &&
      outputPath.endsWith(".html")
    ) {
      let minified = htmlmin.minify(content, {
        useShortDoctype: true,
        removeComments: true,
        collapseWhitespace: true,
      });
      return minified;
    }


    return content;
});

We’re using a transform here which is an Eleventy thing. Transforms can modify a template’s output. At this point, .eleventy.js should look like this:

const htmlmin = require("html-minifier");


module.exports = function (eleventyConfig) {
  eleventyConfig.setUseGitIgnore(false);


  eleventyConfig.addWatchTarget("./_tmp/style.css");


  eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" });


  eleventyConfig.addShortcode("version", function () {
    return String(Date.now());
  });


  eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
    if (
      process.env.ELEVENTY_PRODUCTION &&
      outputPath &&
      outputPath.endsWith(".html")
    ) {
      let minified = htmlmin.minify(content, {
        useShortDoctype: true,
        removeComments: true,
        collapseWhitespace: true,
      });
      return minified;
    }


    return content;
  });
};

Alright, let’s run npm run start once again. You’ll see that nothing has changed and that’s because optimization only happens during build. So, instead, let’s try npm run build and then look at the _site folder. There shouldn’t be a single unnecessary character in the index.html file. The same goes for the style.css file.

A project built like this is now ready to deploy. Good job! 🏆

Integrating Alpine.js

I decided to switch to Eleventy from Gatsby.js because it just felt like too much JavaScript to me. I’m more into the reasonable dose of vanilla JavaScript mixed with Alpine.js. We won’t get into the specifics of Alpine.js here, but it’s worth checking out Hugo DiFrancesco’s primer because it’s a perfect starting point.

Here’s how we can install it to our project from the command line:

npm install --save-dev alpinejs

Now we need to update .eleventy.js with this to the function that passes things through Alpine.js:

eleventyConfig.addPassthroughCopy({
  "./node_modules/alpinejs/dist/alpine.js": "./js/alpine.js",
});

Lastly, we’ll open up _includes/default.njk and add Alpine.js right before the closing </head> tag:

<script src="/js/alpine.js?v={% version %}"></script>

We can check if Alpine is working by adding this to index.njk:

{% extends "_includes/default.njk" %}


{% block title %}It does work{% endblock %}


{% block content %}
  <div class="fixed inset-0 flex justify-center items-center">
    <div>
      <span class="text-change">Good design</span><br/>
      <span class="change">is<br/>as little design<br/>as possible</span><br/>
      <span x-data="{message:'🤖 Hello World 🤓'}" x-text="message"></span>
    </div>
  </div>
{% endblock %}

Launch the project:

npm run start

If Alpine.js works, you’ll see “Hello World” in your browser. Congratulations, times two! 🏆🏆


I hope you can see how quick it can be to set up an Eleventy project, including integrations with Nunjucks for templating, Tailwind for styles and Alpine.js for scripts. I know working with new tech can be overwhelming and even confusing, so feel free to email me at csstricks@gregwolanski.com if you have problems starting up or have an idea for how to simplify this even further.

The post An Eleventy Starter with Tailwind CSS and Alpine.js appeared first on CSS-Tricks.

14:53

We need more inclusive web performance metrics

Scott Jehl argues that performance metrics such as First Contentful Paint and Largest Contentful Paint don’t really capture the full picture of everyone’s experience with websites:

These metrics are often touted as measures of usability or meaning, but they are not necessarily meaningful for everyone. In particular, users relying on assistive technology (such as a screenreader) may not perceive steps in the page loading process until after the DOM is complete, or even later depending on how JavaScript may block that process. Also, a page may not be usable to A.T. until it becomes fully interactive, since many applications often deliver accessible interactivity via external JavaScript

Scott then jots down some thoughts on how we might do that. I think this is always so very useful to keep in mind: what we experience on our site, and what we measure too, might not be the full picture.

Direct Link to ArticlePermalink

The post We need more inclusive web performance metrics appeared first on CSS-Tricks.

Sponsored post
soup-sponsored
09:01

Soup.io will be discontinued :(

Dear soup.io fans and users,
 
today, we have to share very sad news. Soup.io will stop working in less than 10 days. :(
 
It's breaking our heart and we honestly tried whatever we could to keep the platform up and running. But the high costs and low revenue streams made it impossible to continue with it. We invested a lot of personal time and money to operate the platform, but when it's over, it's over.
 
We are really sorry. Soup.io is part of the internet history and online for one and a half decades.
 
Here are the hard facts:
- In 10 days the platform will stop working.
- Backup your data in this time
- We will not keep backups nor can we recover your data
 
July, 20th, 2020 is the due date.
 
Please, share your thoughts and feelings here.
 
Your Soup.io TEAM
Reposted bydotmariuszMagoryannerdanelmangoerainbowzombieskilledmyunicorntomashLogHiMakalesorSilentRulebiauekjamaicanbeatlevuneserenitephinangusiastysmoke11Climbingpragne-ataraksjisauerscharfArchimedesgreywolfmodalnaTheCrimsonIdoljormungundmarbearwaco6mieczuuFeindfeuerDagarhenvairashowmetherainbowszpaqusdivihindsightTabslawujcioBateyelynTabslaensommenitaeliblameyouHalobeatzalicexxxmgnsNorkNork
13:07
Bridging The Gap Between Writers And Content Uploaders
13:00
This Week In Web Design – July 10, 2020
10:50
5 Popular UI Design Trends Everyone Must Know
10:30

A Book Release For Click! And A Chance To Rethink Our Routines

A Book Release For Click! And A Chance To Rethink Our Routines

A Book Release For Click! And A Chance To Rethink Our Routines

Ari Stiles
2020-07-10T10:30:00+00:002020-07-10T11:36:00+00:00

Paul Boag has written three books in the Smashing Library, and Click! How to Encourage Clicks Without Shady Tricks is just the most recent one. Paul wrote some articles for Smashing Magazine in addition to appearing on the Smashing Podcast and SmashingTV to promote the book and talk about some of its themes. Click! finally started shipping in June, and our beloved preorder customers found a surprise in those packages.

This Time, It’s Personal

Paul signed 500 postcards—designed by our own Ricardo Gimenez — and our first 500 buyers each received one of the signed postcards along with their copy of Click!

It was fun to watch the reactions pop up on social media:

Paul’s an experienced author, and we also had a very experienced illustrator for the cover and interior templates—Veerle Pieters! She has contributed artwork to Paul’s other books for Smashing, but she also designed the cover for our first Smashing Magazine Print edition. The illustrations and templates she provided for Click! helped make the book an inspiration to read, too.

A sneak peek into the Click book Photo by Drew McLellan

Working with an experienced author and a seasoned illustrator is always inspiring. There were even a few pleasant surprises along the way:

Creativity In Quarantine

For a lot of us, quarantine has had a profound effect on our productivity levels. Feelings of being overwhelmed might be slowing you down, OR maybe your work is a welcome distraction, and you are getting a lot done right now.

One thing that has changed for most of us is our routines, though. This inevitably leads to some problem solving, which requires some creativity.

Click! comes along at a time when many of us need a creative “nudge.” The book inspires us to think differently about our routines for building online sites and services—what works, and what doesn’t.

“People are under enormous pressure to improve their conversion rates. Marketers have got targets they’ve got to meet, designers are under pressure ... people are inevitably turning to dark patterns. Not because they want to, but because they’re under pressure to. They’re under pressure to bring about results.

So the premise of this book is, first of all, to explain why dark patterns are a bad idea. I talk about it from a purely business point of view, that these are the business reasons why dark patterns are ultimately damaging. And then that inevitably leads to the question of, well, if dark patterns aren’t the answer, then what is?

The majority of the book is exploring what you can do to improve your conversion rate without resorting to these kinds of more manipulative techniques.”

— Paul Boag, Smashing Podcast Episode 12

Plenty Of Inspiration

We want to help you stay inspired! You might like

And, have you looked at Smashing Membership lately? Members have free access to our eBooks, job board, partner offers, SmashingTV webinars, and discounts on just about everything else Smashing has to offer.

More Smashing Books

Promoting best practices and providing you with practical tips to master your daily coding and design challenges has always been (and will be) at the core of everything we do at Smashing.

In the past few years, we were very lucky to have worked together with some talented, caring people from the web community to publish their wealth of experience as printed books that stand the test of time. Trine, Alla and Adam are some of these people. Have you checked out their books already?

Add to cart $39

Add to cart $39

Add to cart $39

Smashing Editorial(ra, il)
09:00

CSS Transitions In Vuejs And Nuxtjs

CSS Transitions In Vuejs And Nuxtjs

CSS Transitions In Vuejs And Nuxtjs

Timi Omoyeni
2020-07-10T09:00:00+00:002020-07-10T11:36:00+00:00

Transitions are a module of CSS that lets you create gradual transitions between the values of specific CSS properties. The behavior of these transitions can be controlled by specifying their timing function, duration, and other attributes. Using these transitions in your applications and websites create a better visual experience and sometimes draws and holds the user’s attention while a piece of information is being introduced to or leaving the screen. According to Can I Use, transitions are supported by most browsers, although there are some minor issues with Internet Explorer and Safari.

Data on support for the css-transitions feature across the major browsers from caniuse.com Source: caniuse.com. (Large preview)

Vue.js is an open-source JavaScript framework for building client-facing web applications and single-page applications (SPA). One of the features of this framework is the ability to add transitions to an app seamlessly, to switch between either pages or components, and we’re going to look at how to do that in this tutorial.

Nuxt.js is also a JavaScript framework, built on top of Vue.js (and often referred to as a framework of a framework), for building server-side web applications, static-generated websites, as well as SPAs. It works the same as Vue.js, so if you know Vue.js, you shouldn’t have many issues getting started with Nuxt.js. Nuxt.js comes with two properties for adding transitions to an app, and we’re going to cover those as well in this tutorial.

This tutorial requires basic knowledge of either Vue.js or Nuxt.js. All of the code snippets in this tutorial can be found on GitHub.

What Is A Transition?

According to the Oxford Dictionary, a transition can be defined as:

“A passage in a piece of writing that smoothly connects two topics or sections to each other.

The process or a period of changing from one state or condition to another.”

In terms of physics, a transition is defined thus:

“A change of an atom, nucleus, electron, etc. from one quantum state to another, with emission or absorption of radiation.”

From these definitions, we get an idea on what a transition is. The definitions all involve two different things or states. In terms of code, a transition is not so different.

What Is A CSS Transition?

According to Mozilla’s web documentation:

“CSS Transitions is a module of CSS that lets you create gradual transitions between the values of specific CSS properties. The behavior of these transitions can be controlled by specifying their timing function, duration, and other attributes.”

This means we can define a CSS transition as: the change in the CSS property of one or more elements from one value to another.

The CSS transition property enables us to add a transition effect to any valid element. It consists of up to four other properties (five, if we count the transition property itself) that can be used individually or combined as a shorthand. Each property has a different function.

transition-property

The transition-property accepts the name of the CSS property that we want to watch out for changes on and whose change process we want to transition. It looks like this:

.btn {
  width: 200px;
  height: 50px;
  transition-property: width;
  background-color: red;
  color: #fff;
  border: 0;
}

But this property does not do anything without the next property.

transition-duration

The transition-duration property specifies the time that the change of the element(s) in the transition-property should go on for. This property is required in order for the transition to work. If it is not set (with a value greater than 0s), then the default value of 0s would mean it would not run. So, let’s set a duration for this transition:

.btn {
  width: 200px;
  transition-property: width;
  transition-duration: 2s;
  background-color: red;
  color: #fff;
  border: 0;
}

Here, we have an element with a class name of btn that has a width of 200px. We are using both the transition-property and the transition-duration properties here. What this means is, “Hey, CSS, watch out for when the width property changes, and when this happens, let the effect take 2s to change.”

So, if we have a button with a class name of btn, then the index.html file would look like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS Transitions</title>
    <link rel="stylesheet" href="./assets/styles.css">
</head>
<body>
    <Section>
        <h1>Hi CSS Transitions</h1>
        <button class="btn">Hover on me to see a cool transition</button>
    </Section>
</body>
</html>

Here, we have an HTML file that contains a button with a class that has transition-property and transition-duration properties watching out for changes to the element’s width.

One thing to note is that, in order for the transition on our button to work, we have to actually change the width of that element, either by manually adjusting the width with the developer tools in the browser, by using one of the CSS pseudo-classes, or by using JavaScript. For the purpose of this tutorial, we’re going to use the CSS pseudo-class :hover to change the width of the button:

// existing styles
.btn:hover {
  width: 300px;
}

Now, if we hover over this button, we should see the width of the button gradually increase over the set time, which is 2s.

See the Pen transition-property and transition-duration by Timi Omoyeni (@timibadass) on CodePen.

transition-timing-function

The transition-timing-function property determines the speed at which the transition effect occurs. Five values are available for this property:

  • ease
    This (the default) specifies a transition effect that starts slowly, then gets fast, then ends slowly.
  • linear
    This specifies a transition effect with the same speed from start to end.
  • ease-in
    This specifies a transition effect with a slow start.
  • ease-out
    This specifies a transition effect with a slow end.
  • ease-in-out
    This specifies a transition effect with a slow start and end.
  • cubic-bezier(n,n,n,n)
    This lets you define your own values in a cubic-bezier function.

So, if we add ease-in to our button, we should notice the speed at which the width and height change, compared to the speed at which the button returns to its normal state. Here is our updated styles.css sheet:

.btn {
  width: 200px;
  height: 50px;
  transition-property: width;
  transition-duration: 2s;
  transition-timing-function: ease-in;
  background-color: red;
  color: #fff;
  border: 0;
}

If we want a more dramatic speed effect or the freedom to set a specific speed effect, we can use cubic-bezier(n,n,n,n):

btn {
    width: 200px;
    height: 50px;
    transition-property: width;
    transition-duration: 2s;
    transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
    background-color: red;
    color: #fff;
    border: 0;
}

See the Pen transition-timing-function by Timi Omoyeni (@timibadass) on CodePen.

One interesting thing about this value is that you can edit it directly in the browser using the developer tools.

cubic bezier in dev tools. Cubic bezier in the browser’s developer tools. (Large preview)

If you click on the highlighted part of your developer tools, you’ll get an interface to modify the cubic-bezier options:

cubic bezier interface highlighted in yellow. Cubic bezier interface highlighted in yellow. (Large preview)

As you move the two points around, the values of (n,n,n,n) change, and you’ll see a representation (highlighted in red) of how the speed effect will appear. This can be very useful when you have a specific speed effect in mind.

transition-delay

The transition-delay property sets how long (in seconds) the transition has to wait before its effect starts to occur. This time is different from the transition-duration property, which specifies how long the transition effect will take place.

.btn {
  width: 200px;
  height: 50px;
  transition-property: width;
  transition-duration: 2s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
  transition-delay: 5s;
  background-color: red;
  color: #fff;
  border: 0;
}

If you try this in the browser, you will notice a delay before the element’s width starts to change. This is because of the transition-delay property and value that we have set.

See the Pen transition-delay by Timi Omoyeni (@timibadass) on CodePen.

Shorthand Property

The individual transition properties can be tedious to use. For this reason, we have the shorthand property: transition. It accepts all of the properties in a defined order:

{
  transition: a b c d;
}

Here, the letters correspond as follows:

  • a: transition-property
  • b: transition-duration
  • c: transition-timing-function
  • d: transition-delay

We can refactor our existing transition to work using this shorthand property:

// from
.btn {
  width: 200px;
  height: 50px;
  transition-property: width;
  transition-duration: 2s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
  transition-delay: 1s;
  background-color: red;
  color: #fff;
  border: 0;
}

// to
.btn {
  width: 200px;
  height: 50px;
  transition: width 2s cubic-bezier(0.075, 0.82, 0.165, 1) 1s;
  background-color: red;
  color: #fff;
  border: 0;
}

If we try this code in the browser, we will get the same transition effect that we got when we used the individual properties.

See the Pen using shorthand property by Timi Omoyeni (@timibadass) on CodePen.

Transitions In Vue.js

Vue.js comes with two different ways to add transitions to an application. This doesn’t mean we cannot use transitions the CSS way. It just means that Vue.js’ developers have built on top of CSS to make it easier to use transitions. Let’s look at them one by one.

Transitioning Individual Elements and Components

One way we can use transitions in Vue.js is by wrapping the transition component around an element or component, using any of the following:

  • conditional rendering ( using v-if),
  • conditional display (using v-show),
  • dynamic components,
  • component root nodes.

When we are developing an application, there are instances when we want to display data to the user depending on a value (such as a boolean). Here’s an example of how this works, taken from the index.vue file:

<template>
  <div>
    <p v-if="show">Now you see me!</p>
    <p v-else>Now you don't!</p>
    <button @click="show = !show">click me</button>
  </div>
</template>
<script>
export default {
 data() {
   return {
     show: true
   }
 }
}
</script>
<style>
</style>

We have added two paragraphs to this page that appear depending on the value of show. We also have a button that changes the value of show when clicked. We’ll add this page to our App.vue file by importing it like so:

<template>
  <div id="app">
    <Index />
  </div>
</template>
<script>
import Index from "./components/index.vue";
export default {
  name: 'App',
  components: {
    Index
  }
}
</script>

If we open the browser, we should see our paragraph and button:

vue landing page when show is true. Vue.js landing page in default state. (Large preview)

Right now, clicking the button changes only the value of show, which causes the visible text to change:

landing page when show is false. Landing page when button is clicked. (Large preview)

Adding a transition to this paragraph can be done by wrapping both paragraphs in the transition component. This component accepts a name prop, which is very important for the transition to work.

<template>
  <div>
    <transition name="fade">
      <p v-if="show">Now you see me!</p>
      <p v-else>Now you don't!</p>
    </transition>
    <button @click="show = !show">click me</button>
  </div>
</template>

This name tells Vue.js which transition to apply to the elements or components inside this transition component. At this point, if we click on the button, we would still not notice any transition because we have yet to add the configuration for our transition in the form of CSS classes.

One thing to note is that, when using a transition between two elements of the same tag, we need to specify a key attribute on each element in order for the transition to occur.

<template>
  <div>
    <transition name="fade">
      <p v-if="show" key="visible">Now you see me!</p>
      <p v-else key="notVisible">Now you don't!</p>
    </transition>
    <button @click="show = !show">click me</button>
  </div>
</template>

Vue.js has six transition classes that are applied to the elements or components inside the transition component, and each of these classes represents a state in the transition process. We’re going to look at only a few of them.

v-enter

The v-enter class represents the “starting state for enter”. This is the point at which a condition (v-if or v-else) has been met and the element is about to be made visible. At this point, the class has been added to the element, and it is removed once the element has been added. The name prop (in this case, fade) attached to the transition component is prefixed to this class name, but without the v. This v can be used by default if name is not provided. Thus, we can add this class to our index.vue file:

<style>
p {
  color: green;
}

.fade-enter{
  color: red;
  transform: translateY(20px);
}
</style>

First, we add a color of green to all of the paragraphs on the page. Then, we add our first transition class, fade-name. Inside this class, we change the color to red, and we make use of the transform and translateY property to move the paragraph by 20px along the y-axis (vertically). If we try clicking on the button again, we will notice that very little or no transition takes place during the switch because we need to add this next class we’ll look at.

v-enter-active

The v-enter-active class represents the “whole entering” state of a transitioning element. It means that this class is added just before the element is inserted or becomes visible, and it is removed when the transition has ended. This class is important for v-enter to work because it can be used to add the CSS transition property to the class, together with its properties (transition-property, transition-duration, transition-timing-function, and transition-delay), some of which are needed in order for the transition effect to work. Let’s add this class to our app and see what happens:

.fade-enter-active {
  transition: transform .3s cubic-bezier(1.0, 0.5, 0.8, 1.0), color .5s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}

See the Pen vue transition enter state by Timi Omoyeni (@timibadass) on CodePen.

Now, if we click on the button, we will notice the transition of the color and the position of each of the texts as they come into view. But the transition from visible to hidden isn’t smooth enough because no transition is happening.

v-leave-active

The v-leave-active class represents the entire state in which an element changes from visible to hidden. This means that this class is applied from the moment an element starts to leave the page, and it is removed once the transition ends. This class is important in order for a leave transition to be applied because it takes in the CSS transition property, which also takes in other transition properties. Let’s add this to our app and see what happens:

.fade-leave-active {
  transition: transform 1s cubic-bezier(1.0, 0.5, 0.8, 1.0), color 1s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}

See the Pen vue transition leave active state by Timi Omoyeni (@timibadass) on CodePen.

When we click on the button now, we will notice that the element that should leave waits for approximately 2 seconds before disappearing. This is because Vue.js is expecting the next class with this transition to be added in order for it to take effect.

v-leave-to

The v-leave-to transition represents the “leaving” state, meaning the point at which an element starts to leave and its transition is triggered. It is added one frame after a leaving transition is triggered, and removed when the transition or animation finishes. Let’s add this class to our app and see what happens:

.fade-leave-to {
  transform: translateX(100px);
  color: cyan;
}

Clicking on the button, we will notice that each element that is leaving is sliding to the right as the color changes.

See the Pen vue transition leave to state by Timi Omoyeni (@timibadass) on CodePen.

Now that we understand how transitions work in Vue.js, here’s an image that brings it all together:

classification of vue transition classes Classification of Vue.js transition classes. (Large preview)

Finally, notice the not-so-smooth transition that occurs during the enter and leave states of elements that are transitioning. This is because Vue.js’ transitions occur simultaneously. Vue.js has a mode prop that helps us achieve a very smooth transition process. This prop accepts one of the following values:

  • in-out
    The new element transitions in first, and then, when it’s complete, the current element transitions out.
  • out-in
    The current element transitions out first, and then, when complete, the new element transitions in.

If we add this mode to our index.vue file and try again, we should see a better transition:

<template>
  <div>
    <transition name="fade" appear mode="out-in">
      <p v-if="show" key="visible">Now you see me!</p>
      <p v-else key="notVisible">Now you don't!</p>
    </transition>
    <button @click="transitionMe">click me</button>
  </div>
</template>

Now, if we click on the button, we will notice that one element leaves before another enters. This is a result of the mode we have selected for this transition. If we try the other mode, we will get a different behavior.

See the Pen vue transition with mode by Timi Omoyeni (@timibadass) on CodePen.

List Transitions

If you ever try adding transitions to more than one element at a time using the transition component, an error will get printed to the console:

Vue transition error printed in console. Vue.js error printed in console. (Large preview)

This is because the transition component is not meant to render more than one element at a time. If we want to transition two or more elements at a time or render a list (using v-for), we make use of the transition-group component. This component also accepts a name prop, but it has some differences from the transition component, including the following:

  • A key attribute is required for each element inside this component.
  • There is no need for the mode prop because more than one element would be rendered at a time.
  • A span element is rendered by default, but it can be modified by specifying a tag prop when defining the transition-group component. Let’s look at an example (in our listTransition.vue file):
    <template>
  <div>
    <h1>List/Group Transition</h1>
    <ul>
      <li v-for="user in users" :key="user.id">
        {{user.name}}
        <button>Remove</button>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      users: [
        {
          name: "Vuejs",
          id: 1
        },
        {
          name: "Vuex",
          id: 2
        },
        {
          name: "Router",
          id: 3
        }
      ]
    };
  }
};
</script>
<style>
</style>

Here, we have an array of users, which we loop through using v-for, displaying the name in our template section. In order to be able to view this list, we need to import this component into the App.vue page:

<template>
  <div id="app">
    <Index />
    <listTransition />
  </div>
</template>
<script>
import Index from "./components/index.vue";
import listTransition from "./components/listTransition.vue";
export default {
  name: "App",
  components: {
    Index,
    listTransition
  }
};
</script>

Note that when using the transition-group component, instead of wrapping our list with a ul tag (or any tag we have in mind), we wrap it around the transition-group component and add the tag to the tag prop, like this:

<template>
  <div>
    <h1>List/Group Transition</h1>
    <transition-group name="slide-fade" tag='ul'>
      <li v-for="user in users" :key="user.id">
        {{user.name}}
        <button>Remove</button>
      </li>
    </transition-group>
  </div>
</template>
<script>
export default {
  data() {
    return {
      users: [
        {
          name: "Vuejs",
          id: 1
        },
        {
          name: "Vuex",
          id: 2
        },
        {
          name: "Router",
          id: 3
        }
      ]
    };
  }
};
</script>
<style>
.slide-fade-enter-active {
  transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1),
    color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-leave-active {
  transition: transform 1s cubic-bezier(1, 0.5, 0.8, 1),
    color 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter {
  color: mediumblue;
  transform: translateY(20px);
}
.slide-fade-leave-to {
  transform: translateX(100px);
  color: cyan;
}
</style>

Here, we have replaced the ul tag with the transition-group component, and added the ul as the tag prop value. If we inspect the updated page in the developer tools, we will see that the list is being wrapped in the element that we specified in the tag prop (that is, ul).

ul tag highlighted in dev tools. The ul tag highlighted in the developer tools. (Large preview)

We have also added a transition name prop with a value of slide-fade to this component, with style rules below in the style section that follow this naming convention. For this to work, we need to add the following lines of code to our file:

<template>
  <div>
    <h1>List/Group Transition</h1>
    <transition-group name="slide-fade" tag="ul">
      <li v-for="user in users" :key="user.id">
        {{user.name}}
        <button @click="removeUser(user.id)">Remove</button>
      </li>
    </transition-group>
  </div>
</template>
<script>
export default {
  // ...
  methods: {
    removeUser(id) {
      let users = this.users.filter(user => user.id !== id);
      this.users = users;
    }
  }
};
</script>
  

In the template section, we add a click event to each button in the loop and pass the user.id to the removeUser method attached to this click event. We then create this function in the script section of our file. This function accepts an id as argument. Then, we run through our existing users and filter out the user with the id passed into this function. When this is done, we save our new array of users to the data of our page.

At this point, if you click on any of the buttons for the users, a transition effect will be applied as the user is being removed from the list.

See the Pen Vue list transition by Timi Omoyeni (@timibadass) on CodePen.

Transitions In Nuxt.js:

Adding transitions to a Nuxt.js application is quite different from how you might be used to in Vue.js. In Nuxt.js, the transition component is automatically added to the application for you. All you need to do is one of the following.

Add It to Individual Page Component

Nuxt.js allows us to add transitions to an individual page component seamlessly. This transition is applied while the user is navigating to this page. All we have to do is add a transition property to the script section of the component. This property can be a string, a function, or an object. Some of the properties it accepts are:

  • name,
  • mode,
  • css.

Like Vue.js, Nuxt.js has a default name that gets assigned to a transition class if no name is provided, and it is called page. Let’s see how this works when we add it to our application in transition.vue:

<template>
  <div>
    <p>
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore libero
      odio, asperiores debitis harum ipsa neque cum nulla incidunt explicabo ut
      eaque placeat qui, quis voluptas. Aut necessitatibus aliquam veritatis.
    </p>
    <nuxt-link to="/">home</nuxt-link>
  </div>
</template>
<script>
export default {
  transition: {
    name: "fade",
    mode: "out-in"
  },
  data() {
    return {
      show: true
    };
  }
};
</script>
<style>
p {
  color: green;
}
.fade-enter-active {
  transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1),
    color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
.fade-leave-active {
  transition: transform 1s cubic-bezier(1, 0.5, 0.8, 1),
    color 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.fade-enter {
  color: mediumblue;
  transform: translateY(20px);
}
.fade-leave-to {
  transform: translateX(100px);
  color: cyan;
}
</style>

On this page, we’ve displayed “lorem ipsum” in the template section. We’ve also added the transition property, to which we have passed an object whose name is set to fade and whose mode is set to out-in. Finally, in the style section, we’ve added some styles that control the transition as the user navigates between this page and another.

In order for this transition to work, we have to navigate to /transition, but we would not notice any transition if we manually enter this route in our browser. So, let’s add a link to this page on the index.vue page.

<template>
  <div class="container">
    <div>
      // ..
      <nuxt-link to="/transition">next page</nuxt-link>
    </div>
  </div>
</template>

Now, if we click the link on either of the two pages, we will notice a sliding transition as the browser moves to and from the /transition route.

pageTransition

Adding transitions to individual pages can be challenging if we want to add them to all of the pages in the application. That’s where pageTransition comes in. This property allows us to add a general configuration for all of our pages in the nuxt.config.js file. This property accepts both a string and object as an option. Let’s see how that works in nuxt.config.js:

export default {
    // ...
    /*
     ** Global CSS
     */
    css: [
        '~/assets/transition.css'
    ],
    pageTransition: {
        name: "fade",
        mode: "out-in"
    },
}

Here, we’ve added the link to a CSS file, which we’ll create shortly. We have also added the pageTransition property to the file, along with with its configuration. Now, let’s create our CSS file, transition.css, and add the following styles to it:

.fade-enter-active {
    transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1), color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
.fade-leave-active {
    transition: transform 1s cubic-bezier(1, 1, 1, 1), color 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.fade-enter {
    color: mediumblue;
    transform: translateY(20px);
}
.fade-leave-to {
    transform: translate3d(-500px, -300px 400px);
    color: cyan;
}

We’ve added the classes and styles that will be applied to the transition between one route and the other. If we get rid of the transition configuration from the transition.vue page and try to navigate between the two pages, we will get a transition effect.

layoutTransition

The layoutTransition property enables us to apply transitions based on the layout that the page is on. It works the same way as pageTranslation, except that it works based on layout. The default transition name is layout. Here’s an example of how it works, in nuxt.config.js:

export default {
    // ...
    /*
     ** Global CSS
     */
    css: [
        '~/assets/transition.css'
    ],
    layoutTransition: {
        name: "fade",
        mode: "out-in"
    },
}

Note that fade has to be the name of the layout in order for the transition to work with its layout. Let’s create this new layout in newLayout.vue to see what I mean:

<template>
  <!-- Your template -->
  <div>
    <h1>new layout</h1>
  </div>
</template>
<script>
export default {
  layout: "blog"
  // page component definitions
};
</script>

Conclusion

We have learned about CSS transitions and how to create them using the transition properties individually (transition-property, transition-duration, transition-timing-function, and transition-delay) and using the shorthand transition property. We have also covered how to apply these transitions in both Vue.js and Nuxt.js. But that’s not all. Vue.js has more ways for us to apply transitions in an application:

Related Resources

Smashing Editorial(ks, ra, yk, il)

July 09 2020

18:54

Memorize Scroll Position Across Page Loads

Hakim El Hattab tweeted a really nice little UX enhancement for a static site that includes a scrollable sidebar of navigation.

The trick is to throw the scroll position into localStorage right before the page is exited, and when loaded, grab that value and scroll to it. I’ll retype it from the tweet…

let sidebar = document.querySelector(".sidebar");

let top = localStorage.getItem("sidebar-scroll");
if (top !== null) {
  sidebar.scrollTop = parseInt(top, 10);
}

window.addEventListener("beforeunload", () => {
  localStorage.setItem("sidebar-scroll", sidebar.scrollTop);
});

What is surprising is that you don’t get a flash-of-wrong-scroll-position. I wonder why? Maybe it has to do with fancy paint holding stuff browsers are doing now? Not sure.

The post Memorize Scroll Position Across Page Loads appeared first on CSS-Tricks.

16:46

11 Best Google Design and Development Tools

Google is always present in one way or another when you are working with Internet or web applications. As applications like Google Chrome are ahead of their time, designers and developers need to analyze how their project is going to function across Google platforms. They need to check how the application may look, what technologies they need to implement, the security issues, and the application’s performance. Google provides users with many efficient Google tools built specifically for that purpose.

Luckily, Google offers access to tools that help any web application to be at its best form. Google’s developer tools are the tools that help developers understand everything about their application’s performance. With these Google tools, you can check designs, real-time editing, JavaScript debugging, and performance measures.

Here is a list of 11 tools offered by Google that are a must to use for designers and developers.

1. Lighthouse:

Lighthouse

Once you have built a website, it is imperative to see if the site performs according to your expectations. You need to check every web page’s performance and functionality. Google provides you with a tool called Lighthouse that aids you in improving the web page’s quality. You can access Lighthouse from the Audits tab. You have multiple options, like desktop and mobile. In addition to this, you can tick boxes to assess performance, SEO, and accessibility factor. With these parameters checked on, the Lighthouse can provide you with a detailed report of its performance and the improvements you can implement.

Lighthouse is an automated and open-source tool that improves the quality of your website’s web pages. You can use Lighthouse for any page of any website, regardless of whether it is publicly accessible or needs authentication. It checks your web pages for parameters like SEO, progressive web applications, performance, and accessibility. You can bring Lighthouse from Chrome’s DevTools, from the command line or as a Node module. You just have to provide Lighthouse with the URL of the web page to run an audit. It provides you with how well the page functions. Each audit execution gives you a reference doc that tells you why an audit is necessary. It also describes how you can fix the errors and problems within your web pages.

2. Polymer:

Polymer

The Polymer is another open-source framework from Google made from an open-source Javascript library. It works better to build web applications from web components. Google developers always work hand-in-hand to provide web developers with a library. With Polymer, you can create customized web elements through simplified ways. Polymer offers you with a one-way as well as two-way data binding feature.

The Polymer is known in the world of web-design widely, but the project has expanded its domain to include a collection of libraries, tools, and standards. Polymer comes with a LitElement editor where defining the web elements becomes easy. Whereas, lit-HTML is an HTML template library that developers can use to write the next-generation, top-level HTML templates in JavaScript. In addition to this, Polymer also provides developers with a PWA starter kit. The PWA starter kit is the original Polymer library along with the sets of web components for web application development.

3. Google API Explorer:

Google API Explorer

Google offers an extensive range of APIs for developers to implement in their web applications. But, looking for them can get tedious. Hence, you should browse Google APIs Explorer helps you find the right API according to your requirement. It provides you with a long list of APIs through which you have to scroll to see the required API. But instead, you can write the API requirement in the provided search box, and it filters out the required APIs. Each link can take you to their reference page, which describes how to use the selected API.4

4. Flutter:

Google tools - Flutter

Flutter is a tool that can help you build aesthetically appealing websites and web applications. If your focus is on a beautiful looking UI, then you should use Flutter to design your UI. With Flutter, you get customizable widgets that help you build native interfaces of your application in a matter of minutes. It has a layered architecture that enables complete customization. All this results in quickly rendering designs that are expressive and flexible. Flutter uses Dart’s native compilers to compile Native ARM machine code.

If you are a  beginner to Flutter, you can browse through their documentation. They have well-equipped documentation that can take you from creation to installation. The applications built with Flutter can be a delightful experience for a user to use. Flutter provides the applications with Cupertino widgets, rich motion API, smooth scrolling system, and platform understanding.

5. Google GitHub:

Google GitHub

Google GitHub is a platform that allows a user to store and exchange the codes and files. With over 260 repositories to browse and look for, Google has a proud place on the platform. They have provided a search box that you can use to look for your required repository and reach the needed file and the code. This search box cuts down on your time and takes you to your required list of repositories. With this tool, you can build, test, and execute along with code management at the same place. You can develop your Actions and its code, or you can work with the existing codes from the community.

Google GitHub is known all over the world to secure the world’s code. Developers, maintainers, and researchers work together on this tool to secure the world’s software supply chain.

6. Puppeteer:

Google tools - Puppeteer

While providing a high-level API, Puppeteer is a Node library developed to control headless Chrome over the DevTools protocol. Whatever you can do manually in a browser can be done in Puppeteer. With Puppeteer, you can click screenshots, create PDFs of pages, and crawl a single-page application.  You can also generate pre-rendered content. It also allows automated form submission, UI test, a keyboard input mechanism, and more.

The Puppeteer can help you create an updated and automated testing platform. It is where you can execute your tests in the latest version of the Chrome browser. You can run these tests that use Javascript and browser features. Performance issues can be corrected using a timeline trace that is available with Puppeteer. Also, you can test the extensions that are provided by Chrome.

7. Workbox:

Workbox

For building PWA, you can start working with Workbox. Workbox comes with a collection of JavaScript libraries that help you add offline support to the web applications. With Workbox’s guides, you can create a service register file, route the incoming requests, and understand the usage of plugins and bundlers. Workbox can help you analyze your performance offline without any network connection by caching the web app’s files and codes. It has the right runtime caching strategy that makes your web apps work accurately, even on an unstable connection. Features like precaching, caching during runtime, and request routing makes Workbox an essential tool in the world of web app development. The most crucial element of Workbox so far is that offline Google Analytics is also available for use to measure your website’s performance.

8. Codelabs:

Google tools - Codelabs

To have a guided tutorial and coding experience, every budding developer and coder should go to Codelabs. The guides on Codelabs provide you the aids in developing a small application. It can also help you extend the functionality of the existing application by adding a new feature. Codelabs is known to improve its user have practical guidance for Google products. The site is structured nicely into categories and events that help you find whatever you want to search.

Among so many code files, it supports Analytics, Android, Assistant, Flutter, Tensor Flow, and Search. You can select one of the options and see the code with which you can quickly develop a small web application. Codelabs can help you build a progressive web application that renders quickly on a slow network. It can have a home screen logo and presents as an excellent, full-screen application experience.

9. Color Tool:

Google tools - Color Tool

Color Tool enables a developer to create, exchange, and use the palette while checking the accessibility. A predefined palette from the Material palette can be useful for your applications. You have to select a color and apply it to the primary color scheme. After that, switch to secondary selection and pick a color again. Finally, you can decide the text color for both the schemes. Color Tool is known for providing the developers and designers with a broad range of color selection. They can use these colors in their apps for designing a vibrant interface.

Color tool is also famous for evaluating the legibility of the text for the chosen color scheme. The assessment is conducted based on the Web Content Accessibility Guidelines. These guidelines focus on the contrast between the text color and the color scheme to help visually impaired people have improved readability.

10. Design Sprints:

Design Sprints

Design Sprints is another Google tool that helps solve problems through designs, prototype development, and having users to test the ideas. It is known to align the teams in a short period with a shared aim. This team has clearly defined goals along with deliverables. Eventually, Design Sprints helps develop a hypothesis, idea prototype, and testing all of it rapidly in a real environment. Originated at Google, it had the vision to encourage UX culture and practicing design leadership within the organization. The resultant framework is a set of flexible methods. The teams are continually working to adapt it according to different organizational structures and goals.

Design Sprints kit helps share the flexible model internally with the Google teams. It is also a repository for design and product development community. This tool is as helpful to beginners as it is to the seasoned developers. The kit includes many  Google tools, templates, recipes, and options to provide your methods.

11. Google DevTools:

Google tools - Google DevTools

Google DevTools is a set of Google tools that incorporate in the Chrome browser. With this tool, you can rapidly edit the pages in real-time and rapidly diagnose the problems which help you optimize websites quickly. Google DevTools is helpful when you want to inspect page elements that make a web page, analyzing CSS and more. Elements Tab introduces Google DevTools. The Elements tab helps you see the code behind the selected web page. It enables you to get an understanding of div or tag on the selected page, and there you can edit the code when it is still live. Google DevTools is the perfect tool when you want to experiment with the designs of a web page. While checking the page layout, you can inspect fonts and see the animations for their behavior.

You can also view and modify the CSS that executes on the back of the page. You can turn on and off the properties to inspect and analyze the designs. Styles Tab provides CSS protocols that apply to a selected component in the DOM tree. With DevTools, you can achieve the right performance and the functionality of the page before applying changes to the live design. DevTools can help you debug underlying JavaScript, analyze and optimize the speed while you inspect the network speed. Chrome is under constant evolution to bring in the latest features.

Conclusion:

Google is not just a search engine, and it is more than that. It provides a developer and a designer with these Google tools you need to design and develop a web app. Sometimes it gets tedious and cumbersome to design and develop a web app that is fast, optimized, engaging as well as good looking. The blog focuses on the list of Google tools that can help you add a lot of value to your web development process.

Consult Lighthouse if you want to improve the performance of your web-based applications. If you need progressive web applications that are visually appealing, Workbox and Flutter can help. The essential merit of using Google tools and resources is that you know all the codes are going to function well with the Chrome browser, which is the most popular browser in the world.

The post 11 Best Google Design and Development Tools appeared first on Line25.

14:52

Building a Blog with Next.js

In this article, we will use Next.js to build a static blog framework with the design and structure inspired by Jekyll. I’ve always been a big fan of how Jekyll makes it easier for beginners to setup a blog and at the same time also provides a great degree of control over every aspect of the blog for the advanced users.

With the introduction of Next.js in recent years, combined with the popularity of React, there is a new avenue to explore for static blogs. Next.js makes it super easy to build static websites based on the file system itself with little to no configuration required.

The directory structure of a typical bare-bones Jekyll blog looks like this:

.
├─── _posts/          ...blog posts in markdown
├─── _layouts/        ...layouts for different pages
├─── _includes/       ...re-usable components
├─── index.md         ...homepage
└─── config.yml       ...blog config

The idea is to design our framework around this directory structure as much as possible so that it becomes easier to  migrate a blog from Jekyll by simply reusing the posts and configs defined in the blog.

For those unfamiliar with Jekyll, it is a static site generator that can transform your plain text into static websites and blogs. Refer the quick start guide to get up and running with Jekyll.

This article also assumes that you have a basic knowledge of React. If not, React’s getting started page is a good place to start.

Installation

Next.js is powered by React and written in Node.js. So we need to install npm first, before adding next, react and react-dom to the project.

mkdir nextjs-blog && cd $_
npm init -y
npm install next react react-dom --save

To run Next.js scripts on the command line, we have to add the next command to the scripts section of our package.json.

"scripts": {
  "dev": "next"
}

We can now run npm run dev on the command line for the first time. Let’s see what happens.

$ npm run dev
> nextjs-blog@1.0.0 dev /~user/nextjs-blog
> next

ready - started server on http://localhost:3000
Error: > Couldn't find a `pages` directory. Please create one under the project root

The compiler is complaining about a missing pages directory in the root of the project. We’ll learn about the concept of pages in the next section.

Concept of pages

Next.js is built around the concept of pages. Each page is a React component that can be of type .js or .jsx which is mapped to a route based on the filename. For example:

File                            Route
----                            -----
/pages/about.js                 /about
/pages/projects/work1.js        /projects/work1
/pages/index.js                 /

Let’s create the pages directory in the root of the project and populate our first page, index.js, with a basic React component.

// pages/index.js
export default function Blog() {
  return <div>Welcome to the Next.js blog</div>
}

Run npm run dev once again to start the server and navigate to http://localhost:3000 in the browser to view your blog for the first time.

Screenshot of the homepage in the browser. The content says welcome to the next.js blog.

Out of the box, we get:

  • Hot reloading so we don’t have to refresh the browser for every code change.
  • Static generation of all pages inside the /pages/** directory.
  • Static file serving for assets living in the/public/** directory.
  • 404 error page.

Navigate to a random path on localhost to see the 404 page in action. If you need a custom 404 page, the Next.js docs have great information.

Screenshot of the 404 page. It says 404 This page could not be found.

Dynamic pages

Pages with static routes are useful to build the homepage, about page, etc. However, to dynamically build all our posts, we will use the dynamic route capability of Next.js. For example:

File                        Route
----                        -----
/pages/posts/[slug].js      /posts/1
                            /posts/abc
                            /posts/hello-world

Any route, like /posts/1, /posts/abc, etc., will be matched by /posts/[slug].js and the slug parameter will be sent as a query parameter to the page. This is especially useful for our blog posts because we don’t want to create one file per post; instead we could dynamically pass the slug to render the corresponding post.

Anatomy of a blog

Now, since we understand the basic building blocks of Next.js, let’s define the anatomy of our blog.

.
├─ api
│  └─ index.js             # fetch posts, load configs, parse .md files etc
├─ _includes
│  ├─ footer.js            # footer component
│  └─ header.js            # header component
├─ _layouts
│  ├─ default.js           # default layout for static pages like index, about
│  └─ post.js              # post layout inherts from the default layout
├─ pages
│  ├─ index.js             # homepage
|  └─ posts                # posts will be available on the route /posts/
|     └─ [slug].js       # dynamic page to build posts
└─ _posts
   ├─ welcome-to-nextjs.md
   └─ style-guide-101.md

Blog API

A basic blog framework needs two API functions: 

  • A function to fetch the metadata of all the posts in _posts directory
  • A function to fetch a single post for a given slug with the complete HTML and metadata

Optionally, we would also like all the site’s configuration defined in config.yml to be available across all the components. So we need a function that will parse the YAML config into a native object.

Since, we would be dealing with a lot of non-JavaScript files, like Markdown (.md), YAML (.yml), etc, we’ll use the raw-loader library to load such files as strings to make it easier to process them. 

npm install raw-loader --save-dev

Next we need to tell Next.js to use raw-loader when we import .md and .yml file formats by creating a next.config.js file in the root of the project (more info on that).

module.exports = {
  target: 'serverless',
  webpack: function (config) {
    config.module.rules.push({test:  /\.md$/, use: 'raw-loader'})
    config.module.rules.push({test: /\.yml$/, use: 'raw-loader'})
    return config
  }
}

Next.js 9.4 introduced aliases for relative imports which helps clean up the import statement spaghetti caused by relative paths. To use aliases, create a jsconfig.json file in the project’s root directory specifying the base path and all the module aliases needed for the project.

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@includes/*": ["_includes/*"],
      "@layouts/*": ["_layouts/*"],
      "@posts/*": ["_posts/*"],
      "@api": ["api/index"],
    }
  }
}

For example, this allows us to import our layouts by just using:

import DefaultLayout from '@layouts/default'

Fetch all the posts

This function will read all the Markdown files in the _posts directory, parse the front matter defined at the beginning of the post using gray-matter and return the array of metadata for all the posts.

// api/index.js
import matter from 'gray-matter'


export async function getAllPosts() {
  const context = require.context('../_posts', false, /\.md$/)
  const posts = []
  for(const key of context.keys()){
    const post = key.slice(2);
    const content = await import(`../_posts/${post}`);
    const meta = matter(content.default)
    posts.push({
      slug: post.replace('.md',''),
      title: meta.data.title
    })
  }
  return posts;
}

A typical Markdown post looks like this:

---
title:  "Welcome to Next.js blog!"
---
**Hello world**, this is my first Next.js blog post and it is written in Markdown.
I hope you like it!

The section outlined by --- is called the front matter which holds the metadata of the post like, title, permalink, tags, etc. Here’s the output:

[
  { slug: 'style-guide-101', title: 'Style Guide 101' },
  { slug: 'welcome-to-nextjs', title: 'Welcome to Next.js blog!' }
]

Make sure you install the gray-matter library from npm first using the command npm install gray-matter --save-dev.

Fetch a single post

For a given slug, this function will locate the file in the _posts directory, parse the Markdown with the marked library and return the output HTML with metadata.

// api/index.js
import matter from 'gray-matter'
import marked from 'marked'


export async function getPostBySlug(slug) {
  const fileContent = await import(`../_posts/${slug}.md`)
  const meta = matter(fileContent.default)
  const content = marked(meta.content)    
  return {
    title: meta.data.title, 
    content: content
  }
}

Sample output:

{
  title: 'Style Guide 101',
  content: '<p>Incididunt cupidatat eiusmod ...</p>'
}

Make sure you install the marked library from npm first using the command npm install marked --save-dev.

Config

In order to re-use the Jekyll config for our Next.js blog, we’ll parse the YAML file using the js-yaml library and export this config so that it can be used across components.

// config.yml
title: "Next.js blog"
description: "This blog is powered by Next.js"


// api/index.js
import yaml from 'js-yaml'
export async function getConfig() {
  const config = await import(`../config.yml`)
  return yaml.safeLoad(config.default)
}

Make sure you install js-yaml from npm first using the command npm install js-yaml --save-dev.

Includes

Our _includes directory contains two basic React components, <Header> and <Footer>, which will be used in the different layout components defined in the _layouts directory.

// _includes/header.js
export default function Header() {
  return <header><p>Blog | Powered by Next.js</p></header>
}


// _includes/footer.js
export default function Footer() {
  return <footer><p>©2020 | Footer</p></footer>
}

Layouts

We have two layout components in the _layouts directory. One is the <DefaultLayout> which is the base layout on top of which every other layout component will be built.

// _layouts/default.js
import Head from 'next/head'
import Header from '@includes/header'
import Footer from '@includes/footer'


export default function DefaultLayout(props) {
  return (
    <main>
      <Head>
        <title>{props.title}</title>
        <meta name='description' content={props.description}/>
      </Head>
      <Header/>
      {props.children}
      <Footer/>
    </main>
  )
}

The second layout is the <PostLayout> component that will override the title defined in the <DefaultLayout> with the post title and render the HTML of the post. It also includes a link back to the homepage.

// _layouts/post.js
import DefaultLayout from '@layouts/default'
import Head from 'next/head'
import Link from 'next/link'


export default function PostLayout(props) {
  return (
    <DefaultLayout>
      <Head>
        <title>{props.title}</title>
      </Head>
      <article>
        <h1>{props.title}</h1>
        <div dangerouslySetInnerHTML={{__html:props.content}}/>
        <div><Link href='/'><a>Home</a></Link></div> 
      </article>
    </DefaultLayout>
  )
}

next/head is a built-in component to append elements to the <head> of the page. next/link is a built-in component that handles client-side transitions between the routes defined in the pages directory.

Homepage

As part of the index page, aka homepage, we will list all the posts inside the _posts directory. The list will contain the post title and the permalink to the individual post page. The index page will use the <DefaultLayout> and we’ll import the config in the homepage to pass the title and description to the layout.

// pages/index.js
import DefaultLayout from '@layouts/default'
import Link from 'next/link'
import { getConfig, getAllPosts } from '@api'


export default function Blog(props) {
  return (
    <DefaultLayout title={props.title} description={props.description}>
      <p>List of posts:</p>
      <ul>
        {props.posts.map(function(post, idx) {
          return (
            <li key={idx}>
              <Link href={'/posts/'+post.slug}>
                <a>{post.title}</a>
              </Link>
            </li>
          )
        })}
      </ul>
    </DefaultLayout>
  )
} 


export async function getStaticProps() {
  const config = await getConfig()
  const allPosts = await getAllPosts()
  return {
    props: {
      posts: allPosts,
      title: config.title,
      description: config.description
    }
  }
}

getStaticProps is called at the build time to pre-render pages by passing props to the default component of the page. We use this function to fetch the list of all posts at build time and render the posts archive on the homepage.

Screenshot of the homepage showing the page title, a list with two post titles, and the footer.

Post page

This page will render the title and contents of the post for the slug supplied as part of the context. The post page will use the <PostLayout> component.

// pages/posts/[slug].js
import PostLayout from '@layouts/post'
import { getPostBySlug, getAllPosts } from "@api"


export default function Post(props) {
  return <PostLayout title={props.title} content={props.content}/>
}


export async function getStaticProps(context) {
  return {
    props: await getPostBySlug(context.params.slug)
  }
}


export async function getStaticPaths() {
  let paths = await getAllPosts()
  paths = paths.map(post => ({
    params: { slug:post.slug }
  }));
  return {
    paths: paths,
    fallback: false
  }
}

If a page has dynamic routes, Next.js needs to know all the possible paths at build time. getStaticPaths supplies the list of paths that has to be rendered to HTML at build time. The fallback property ensures that if you visit a route that does not exist in the list of paths, it will return a 404 page.

Screenshot of the blog page showing a welcome header and a hello world blue above the footer.

Production ready

Add the following commands for build and start in package.json, under the scripts section and then run npm run build followed by npm run start to build the static blog and start the production server.

// package.json
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

The entire source code in this article is available on this GitHub repository. Feel free to clone it locally and play around with it. The repository also includes some basic placeholders to apply CSS to your blog.

Improvements

The blog, although functional, is perhaps too basic for most average cases. It would be nice to extend the framework or submit a patch to include some more features like:

  • Pagination
  • Syntax highlighting
  • Categories and Tags for posts
  • Styling

Overall, Next.js seems really very promising to build static websites, like a blog. Combined with its ability to export static HTML, we can built a truly standalone app without the need of a server!

The post Building a Blog with Next.js appeared first on CSS-Tricks.

14:50

Frontity is React for WordPress

Some developers just prefer working in React. I don’t blame them really, because I like React too. Maybe that’s what they learned first. I’ve been using it long enough there is just some comfort to it. But mostly it is the strong component model that I like. There is just something nice about a codebase where things are constructed from components with clear jobs and responsibilities.

It’s not terribly common to see WordPress sites built with React though. The standard way to use WordPress is through themes that are essentially styles and PHP files that handle the templating. Frontity is changing that though. Frontity is a React-powered framework that digests your WordPress site’s API and builds the entire front end in React with all the powerful tools you’ve come to expect from that type of environment.

OMG, Now That’s a Fast Setup

This is how I was able to get started. At the command line, I did:

npx frontity create my-app

Then I went into the folder it created and did:

npx frontity dev

That instantly spins up a site for you to start working with.

To make it feel more real for me, I did went into frontity.settings.js and changed the source API to point at CSS-Tricks:

{
  name: "@frontity/wp-source",
  state: {
    source: {
      api: "https://css-tricks.com/wp-json",
    },
  },
},

And now look at what I get:

That’s wild. For some projects, that’s straight up ready to deploy.

Check out their intro video which steps through this exact thing

Getting to Work

My first instinct with things like this is to get my hands into the styling right away. The theme that installs by default is the Mars theme and they have a nice guide to help wrap your mind around how it works. The theme uses Emotion for styling, so the components have styles you can mess with right in them. I found the <HeadContainer> component in index.js and immediately did the background: red change!

const HeadContainer = styled.div`
  display: flex;
  align-items: center;
  flex-direction: column;
  background-color: red;
`;

It hot-module-reloaded that sucker instantly:

Is this one of those client-side only technologies?

That’s what I thought to myself. I mean, one of the advantages of using WordPress as-is is that you get the server rendering for free. That means no SEO worries (we know client-side rendered sites can take a week or more to be crawled for every change). That means resiliency and speed.

Frontity does do server side rendering! It uses Isomorphic rendering, meaning you need a Node server to render the pages, but that means the browser will get fully formed HTML for pages!

It’s a perfect match for Vercel, basically.

Similarly to how easy a new site is to scaffold and run in development, all you have to do to prep it for production is:

npx frontity build

Then run the Node server:

npx frontity serve

Cool.

I also really like that there is community around all this. If you need help, you’ll get it.

This is a best-of-all-worlds scenario.

I’m always very happy building sites with WordPress, and doubly so now that we have the block editor to use. I really like having an editor experience that helps me write and craft the kind of pages I want to create.

But I also like working with component-based architectures that have fast, easy-to-use, hot refreshing local development environments. Once you work in this kind of dev environment, it’s hard to use anything else! Beautiful DX.

And I also also want to make damn sure the sites I deploy to production are fast, robust, resilient, accessible, and SEO friendly.

I’d get all that with a Frontity site.


Another thing I like here is that Automattic themselves is on board with all this. Not just in spirit, but they are literally big investors. I think they are very smart to see this as an important part of the WordPress ecosystem. Building with WordPress doesn’t mean not building with React, especially with Frontity doing so much of the heavy lifting.

The post Frontity is React for WordPress appeared first on CSS-Tricks.

13:03
What Is Workflow
11:13
How to Find Your First Web Design Client in 48 Hours or Less
11:00

Removing Panic From E-Commerce Shipping And Inventory Alerts

Removing Panic From E-Commerce Shipping And Inventory Alerts

Removing Panic From E-Commerce Shipping And Inventory Alerts

Suzanne Scacca
2020-07-09T11:00:00+00:002020-07-09T16:34:18+00:00

When it comes to displaying shipping and inventory alerts on an e-commerce website, you have to be very careful about inciting panic in your shoppers.

“Item is out of stock.”

“Expect shipping delays.”

“Page does not exist.”

These words alone are enough to turn a pleasant shopping experience into a panicked and frustrating one.

You have to be very careful about how you design these notices on your site, too. You obviously want to inform visitors of changes that impact their shopping experience, but you don’t want panic to be the resulting emotion when your alert is seen.

Better Search UX

For large-scale and e-commerce sites, the search experience is an increasingly critical tool. You can vastly improve the experience for users with thoughtful microcopy and the right contextualization. Read a related article →

When people panic, the natural response is to find a way to regain some control over the situation. The problem is, that regained control usually comes at the expense of the business’s profits, trust, and customer loyalty.

Unfortunately, some of the design choices we make in terms of alerts can cause undue panic.

If you want to better control your shoppers’ responses and keep them on the path to conversion, keep reading.

Note: Because the following post was written during the coronavirus pandemic, many of the alerts you’re going to see are related to it. However, that doesn’t mean these tips aren’t valid for other panic-inducing situations — like when a storm destroys an area and it’s impossible to order anything in-store or online or like during the November shopping frenzy when out-of-stock inventory is commonplace.

1. Avoid Out-Of-Stock Notices When Possible

Colleen Kirk is a professor of marketing at the New York Institute of Technology and an expert on territorial shopping.

She has a great analogy to help us understand why this happens:

Have you ever felt as if another driver stole your parking spot or were upset when someone else nabbed the last sweater that you had your eye on? If so, you experienced psychological ownership. You can feel psychological ownership over pretty much anything that doesn’t belong to you, from the last chocolate truffle in a display case to the dream home you found on Zillow and even intangible things like ideas.

When it comes to shopping in person, people exhibit territorial shopping traits by placing their items in a shopping cart. Or when they put a separator bar between their items on a conveyor belt and the person’s behind them.

We don’t really have that luxury on a website. The best we can do is enable shoppers to save their items to their cart or a wishlist, but that doesn’t keep the items from selling out before they have a chance to buy them.

This can lead to huge problems, especially when a shopper has gotten excited about that toy or shirt they “put aside”, only to discover a few hours later that the item is gone or no longer available.

The worst thing you could do is to remove out-of-stock product pages. You don’t want shoppers running into a 404 page and experiencing not just panic but also confusion and frustration.

While not as bad, I’d also recommend against displaying an inactive “Sold Out” or “Out of Stock” notice or button to shoppers as Olivela has done here:

Olivela 'sold out' notice and inactive button for upcycled denim mask The Olivela website displays an inactive 'Sold Out' button for its denim mask product. (Source: Olivela) (Large preview)

Seeing this kind of feels like Google Maps directing you to your destination, only for you to end up at the edge of a lake where there’s supposed to be a road.

“Why the heck did you even send me here?”, is what your shoppers are going to wonder. And then they’re going to have to turn around and try to find something comparable to buy instead.

There are better ways to handle this that will also reduce the chances of your shoppers panic-buying a second-rate substitute for the item they really wanted. And this plays into the idea of territorial shopping (but in a healthy way).

This is what Target does when an item is “temporarily out of stock”:

Target out of stock product - 'Notify me when it’s back' button Target provides shoppers with the option to get notified when out-of-stock items become available. (Source: Target) (Large preview)

Rather than display an unclickable button and just completely shut down shoppers’ hopes of getting the item, a “Notify me when it’s back” button is presented. This is a more positive approach to handling out-of-stock inventory and, if customers really want the item, they’re more likely to use it rather than settle for something else or try another site altogether. (I’ll talk about the “Pick it up” option down below.)

Another way you can handle this is to do what Summersalt does and turn your “Sold Out” button into a “Pre-order” one:

Summersalt 'Pre-order' button for out-of-stock items Summersalt provides shoppers with 'Pre-order' button for out-of-stock items. (Source: Summersalt) (Large preview)

What’s nice about this button is that it not only empowers shoppers to secure the currently unavailable item they have their eyes on, but it tells them exactly when they will get it.

So, if you know when inventory will be restored, this is a better option than the “Notify” button as there’s more transparency about availability.

2. Don’t Over-Promise Or Give Your Shoppers False Hope

It’s a good idea to keep your shoppers informed about how external situations may impact their online shopping experience. And you can use key areas like the website’s sticky banner or a promotional banner on the homepage to do that.

That said, be very careful what you promote.

Special notices aren’t the kinds of things that get ignored the way that cookie consent notices or lead generation pop-ups do.

Consumers know to look at these areas for things like promo codes and sales event details.

If you put anything else in there, you better make sure the notice is positive, useful and truthful.

Let’s take a look at what’s going on with the Gap website:

Gap’s 'Taking Care Takes Time' banner notice on website Gap’s 'Taking Care Takes Time' notice addresses shipping delays due to COVID-19. (Source: Gap) (Large preview)

Gap has three notices that appear in the header of its site:

  • In black: “Free shipping on $25+, free returns on all orders.”
  • In white: “Taking Care Takes Time. We’ve implemented special procedures as we work to keep our teams — and you — safe, so shipping of orders may be delayed.”
  • In blue: “Extra 50% off markdowns + more.”

It’s overwhelming. And it’s almost as if they wanted the alert in the middle to be ignored (or missed altogether) as it’s sandwiched between two very sharp-looking banners that promote attractive deals.

If your alert is related to something that’s going to affect the shoppers’ experience, don’t bury it and don’t try to massage it with overly optimistic messaging. Also, don’t contradict it with another alert that suggests the first one isn’t one to worry about.

Here’s why I say that:

Gap promotion for 'Our Masks are Back' during COVID-19 Gap promotes 'Our Masks are Back' on the homepage of its website. (Source: Gap) (Large preview)

This message is problematic for a couple of reasons. For one, Gap’s masks aren’t really “back” if they’re only available for pre-order. Second, it runs contrary to the top banner’s message about shipping delays.

Unsurprisingly, shoppers did not react well to this hyped-up announcement:

Gap backlash on Facebook during COVID-19 Gap receives an extraordinary amount of backlash on Facebook over mismanagement of shipping and promotions. (Source: Gap) (Large preview)

When Facebook ran a promotion on Facebook about the masks, it received a huge amount of backlash from customers. Many customers didn’t want to hear about the masks because they’d been waiting over a month for existing orders to ship and couldn’t get a customer service rep to talk to them. There were also complaints about items being canceled from their orders, only for them to magically become “available” again in the store.

A website that’s handling similar circumstances well right now is Urban Outfitters:

Urban Outfitters curbside pickup - sticky notice on website Urban Outfitters is focusing on the positive of a bad situation. (Source: Urban Outfitters) (Large preview)

Urban Outfitters, like a lot of non-essential retailers right now, has had to make some adjustments in order to stay alive. But rather than displaying a notice alerting online shoppers to shipping delays like many of its counterparts, Urban Outfitters has turned it into a positive.

The banner reads: “Your local UO is now offering curbside pickup!”

There’s no hidden message here that tries to explain away the brand’s bad behavior. There’s no greedy cash-grab, promising deep discounts for people who shop with them despite likely delays in shipping. There’s no overhyping of a promise they can’t possibly keep.

This is an actionable offer.

What I’d suggest for e-commerce businesses that want to keep customers on their side — even during a tumultuous time — is to keep their alerts simple, honest and useful. And if you’re going to promote multiple ones, make sure they tell the same story.

3. Soften The Blow By Promoting Multichannel Options

I recently signed a new lease on an apartment but was dismayed to discover that my move-in date and furniture delivery date wouldn’t line up because of shipping delays. I was well-aware of this when I made the purchase, but I wasn’t as prepared as I wanted to be. And because we were in the middle of a city-wide lockdown, I was unable to crash with friends who live over the state border.

I thought, “I’ll sleep on an air mattress. No biggie.” But then remembered I left mine behind in Delaware. So, I had to figure out a way to buy a cheap air mattress.

All of my go-to e-commerce websites had shipping delay alerts. And as I perused their available products (which there were very few), my panic only worsened. Until I discovered a website that offered BOPIS.

Buy-online-pickup-in-store is a shopping trend that’s proven very popular with consumers. According to data from Doddle and a report from Business Insider:

  • 68% of U.S. shoppers have used BOPIS on numerous occasions.
  • 50% have chosen where to shop based on BOPIS availability.
  • Shipping costs (avoiding them), speed and convenience are the primary reasons why customers prefer to buy online and shop in-store. It’s the best of both worlds.

If you’re building an e-commerce site for a company with retail locations that do this, then make sure you get customers thinking about it right away.

Barnes and Noble, for instance, enables shoppers to set their preferred local store:

Barnes & Noble 'Change My Store' - location selection Barnes & Noble allows shoppers to set their preferred retail store location. (Source: Barnes and Noble) (Large preview)

This is a great feature to have. The only thing I’d do differently is to place a button in the header of the site that immediately takes them to the “Change My Store” or “Find Store” pop-up or page. That way, shoppers don’t have to wait until they’ve found a product they like to discover whether or not it’s available at their store for pickup.

Once a user has set their store location, though, Barnes & Noble remembers it and uses it to enhance the remainder of the shopping experience:

Barnes & Noble search listings with online and in-store availability info Barnes & Noble uses shopper location information to enhance search listings. (Source: Barnes and Noble) (Large preview)

This is what search listings look like for “Magna Tiles” on the Barnes & Noble site when the user sets their search to “List” view. Pretty neat, right? While “Grid” view is standard, only showing the featured image, product name and price, this view provides extra detail about availability.

This can provide a ton of relief for shoppers who too-often encounter “out-of-stock” or “not available in store” notices on individual product pages. This way, they can spend their time looking at the items they can get the most quickly and conveniently. There are no surprises.

If cross-channel options are available — between website and mobile app, website and retail store, website and third-party delivery service — make sure you call attention to them early on so customers can take advantage of the streamlined shopping experience.

Wrapping Up

Panicked shopping can lead to serious issues for your e-commerce site. Rather than put your shoppers in a situation where they grow impatient, dissatisfied, and frustrated by the site that seems to put up barriers at every turn, design your alerts so that they turn a bad experience into a positive one.

Smashing Editorial(ra, yk, il)
07:21
3 Ways To Build Backlinks Without Writing Content

July 08 2020

21:29

A little bit of plain Javascript can do a lot

Julia Evans:

I decided to implement almost all of the UI by just adding & removing CSS classes, and using CSS transitions if I want to animate a transition.

An awful lot of the JavaScript on sites (that aren’t otherwise entirely constructed from JavaScript) is click the thing, toggle the class — which is why jQuery was so good and libraries like Alpine.js are finding happy developer audiences.

I once did a screencast called “Hey designers, if you only know one thing about JavaScript, this is what I would recommend which was basically: learn to toggle classes. From that:

Sometimes, to start a journey into learning something huge and complex, you need to learn something small and simple. JavaScript is huge and complex, but you can baby step into it by learning small and simple things. If you’re a web designer, I think there is one thing in particular that you can learn that is extremely empowering.

This is the thing I want you to learn: When you click on some element, change a class on some element.

Direct Link to ArticlePermalink

The post A little bit of plain Javascript can do a lot appeared first on CSS-Tricks.

14:58
How SEO Commerce Helps Business Thrive During Pandemic
14:46

How to Make a List Component with Emotion

I’ve been doing a bit of refactoring this week at Sentry and I noticed that we didn’t have a generic List component that we could use across projects and features. So, I started one, but here’s the rub: we style things at Sentry using Emotion, which I have only passing experience with and is described in the docs as…

[…] a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

If you’ve never heard of Emotion, the general idea is this: when we’re working on big codebases with lots of components, we want to ensure that we can control the cascade of our CSS. So, let’s say you have an .active class in one file and you want to make sure that doesn’t impact the styles of a completely separate component in another file that also has a class of.active.

Emotion tackles this problem by adding custom strings to your classnames so they don’t conflict with other components. Here’s an example of the HTML it might output:

<div class="css-1tfy8g7-List e13k4qzl9"></div>

Pretty neat, huh? There’s lots of other tools and workflows out there though that do something very similar, such as CSS Modules.

To get started making the component, we first need to install Emotion into our project. I’m not going to walkthrough that stuff because it’s going to be different depending on your environment and setup. But once that’s complete we can go ahead and create a new component like this:

import React from 'react';
import styled from '@emotion/styled';

export const List = styled('ul')`
  list-style: none;
  padding: 0;
`;

This looks pretty weird to me because, not only are we writing styles for the <ul> element, but we’re defining that the component should render a <ul>, too. Combining both the markup and the styles in one place feels odd but I do like how simple it is. It just sort of messes with my mental model and the separation of concerns between HTML, CSS, and JavaScript.

In another component, we can import this <List> and use it like this:

import List from 'components/list';

<List>This is a list item.</List>

The styles we added to our list component will then be turned into a classname, like .oefioaueg, and then added to the <ul> element we defined in the component.

But we’re not done yet! With the list design, I needed to be able to render a <ul> and an <ol> with the same component. I also needed a version that allows me to place an icon within each list item. Just like this:

The cool (and also kind of weird) thing about Emotion is that we can use the as attribute to select which HTML element we’d like to render when we import our component. We can use this attribute to create our <ol> variant without having to make a custom type property or something. And that happens to look just like this:

<List>This will render a ul.</List>
<List as="ol">This will render an ol.</List>

That’s not just weird to me, right? It’s super neat, however, because it means that we don’t have to do any bizarro logic in the component itself just to change the markup.

It was at this point that I started to jot down what the perfect API for this component might look like though because then we can work our way back from there. This is what I imagined:

<List>
  <ListItem>Item 1</ListItem>
  <ListItem>Item 2</ListItem>
  <ListItem>Item 3</ListItem>
</List>

<List>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem>
</List>

<List as="ol">
  <ListItem>Item 1</ListItem>
  <ListItem>Item 2</ListItem>
  <ListItem>Item 3</ListItem>
</List>

So after making this sketch I knew we’d need two components, along with the ability to nest icon subcomponents within the <ListItem>. We can start like this:

import React from 'react';
import styled from '@emotion/styled';

export const List = styled('ul')`
  list-style: none;
  padding: 0;
  margin-bottom: 20px;

  ol& {
    counter-reset: numberedList;
  }
`;

That peculiar ol& syntax is how we tell emotion that these styles only apply to an element when it’s rendered as an <ol>. It’s often a good idea to just add a background: red; to this element to make sure your component is rendering things correctly.

Next up is our subcomponent, the <ListItem>. It’s important to note that at Sentry we also use TypeScript, so before we define our <ListItem> component, we’ll need to set our props up first:

type ListItemProps = {
  icon?: React.ReactNode;
  children?: string | React.ReactNode;
  className?: string;
};

Now we can add our <IconWrapper> component that will size an <Icon> component within the ListItem. If you remember from the example above, I wanted it to look something like this:

<List>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>
  <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem>
</List>

That IconBusiness component is a preexisting component and we want to wrap it in a span so that we can style it. Thankfully, we’ll need just a tiny bit of CSS to align the icon properly with the text and the <IconWrapper> can handle all of that for us:

type ListItemProps = {
  icon?: React.ReactNode;
  children?: string | React.ReactNode;
  className?: string;
};

const IconWrapper = styled('span')`
  display: flex;
  margin-right: 15px;
  height: 16px;
  align-items: center;
`;

Once we’ve done this we can finally add our <ListItem> component beneath these two, although it is considerably more complex. We’ll need to add the props, then we can render the <IconWrapper> above when the icon prop exists, and render the icon component that’s passed into it as well. I’ve also added all the styles below so you can see how I’m styling each of these variants:

export const ListItem = styled(({icon, className, children}: ListItemProps) => (
  <li className={className}>
    {icon && (
      <IconWrapper>
        {icon}
      </IconWrapper>
    )}
    {children}
  </li>
))<ListItemProps>`
  display: flex;
  align-items: center;
  position: relative;
  padding-left: 34px;
  margin-bottom: 20px;
	
  /* Tiny circle and icon positioning */
  &:before,
	& > ${IconWrapper} {
    position: absolute;
    left: 0;
  }

  ul & {
    color: #aaa;
    /* This pseudo is the tiny circle for ul items */ 
    &:before {
      content: '';
      width: 6px;
      height: 6px;
      border-radius: 50%;
      margin-right: 15px;
      border: 1px solid #aaa;
      background-color: transparent;
      left: 5px;
      top: 10px;
    }
		
    /* Icon styles */
    ${p =>
      p.icon &&
      `
      span {
        top: 4px;
      }
      /* Removes tiny circle pseudo if icon is present */
      &:before {
        content: none;
      }
    `}
  }
  /* When the list is rendered as an <ol> */
  ol & {
    &:before {
      counter-increment: numberedList;
      content: counter(numberedList);
      top: 3px;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      width: 18px;
      height: 18px;
      font-size: 10px;
      font-weight: 600;
      border: 1px solid #aaa;
      border-radius: 50%;
      background-color: transparent;
      margin-right: 20px;
    }
  }
`;

And there you have it! A relatively simple <List> component built with Emotion. Although, after going through this exercise I’m still not sure that I like the syntax. I reckon it sort of makes the simple stuff really simple but the medium-sized components much more complicated than they should be. Plus, it could be pretty darn confusing to a newcomer and that worries me a bit.

But everything is a learning experience, I guess. Either way, I’m glad I had the opportunity to work on this tiny component because it taught me a few good things about TypeScript, React, and trying to make our styles somewhat readable.

The post How to Make a List Component with Emotion appeared first on CSS-Tricks.

14:46

How to delete all node_modules directories from your computer

Nice tip from Chris Ferdinandi:

My node_modules directories contained 50mb of stuff on the small side, and over 200mb of files in some cases. Over a few dozen projects, that really adds up!

Two dozen projects with 200 MB worth of node_modules? That’s nearly 5 GB of space for a bunch of stuff you’ve probably forgotten is even there, isn’t doing anything, and if you need again is a single command away. I feel like there should almost be a reaper app for these folders, deleting them if they haven’t been touched in a few weeks.

Nuke ’em:

# Mac/Linux
find . -name "node_modules" -type d -prune -print | xargs du -chs

# Windows
FOR /d /r . %d in (node_modules) DO @if EXIST "%d" echo %d"

Direct Link to ArticlePermalink

The post How to delete all node_modules directories from your computer appeared first on CSS-Tricks.

13:00
Create Simple Parallax Scrolling Effect in Hero Section using Pure CSS
Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.
(PRO)
No Soup for you

Don't be the product, buy the product!

close
YES, I want to SOUP ●UP for ...