Web Standards

Gulp for WordPress: Creating the Tasks

Css Tricks - Thu, 12/27/2018 - 5:11am

This is the second post in a two-part series about creating a Gulp workflow for WordPress theme development. Part one focused on the initial installation, setup, and organization of Gulp in a WordPress theme project. This post goes deep into the tasks Gulp will run by breaking down what each task does and how to tailor them to streamline theme development.

Now that we spent the first part of this series setting up a WordPress theme project with Grunt installed in it, it's time to dive into the tasks we want it to do for us as we develop the theme. We're going to get our hands extremely dirty in this post, get ready to write some code!

Article Series:
  1. Initial Setup
  2. Creating the Tasks (This Post)
Creating the style task

Let’s start by compiling src/bundle.scss from Sass to CSS, then minifying the CSS output for production mode and putting the completed bundle.css file into the dist directory.

We’re going to use a couple of Gulp plugins to do the heavy lifting. We’ll use gulp-sass to compile things and gulp-clean-css to minify. Then, gulp-if will allow us to conditionally run functions which, In our case, will check if we are in production or development modes before those tasks run and then execute accordingly.

We can install all three plugins in one fell swoop:

npm install --save-dev gulp-sass gulp-clean-css gulp-if

Let’s make sure we have something in our bundle.scss file so we can test the tasks:

$colour: #f03; body { background-color: $colour; }

Alright, back to the Gulpfile to import the plugins and define the task that runs them:

import { src, dest } from 'gulp'; import yargs from 'yargs'; import sass from 'gulp-sass'; import cleanCss from 'gulp-clean-css'; import gulpif from 'gulp-if'; const PRODUCTION = yargs.argv.prod; export const styles = () => { return src('src/scss/bundle.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'}))) .pipe(dest('dist/css')); }

Let’s walk through this code to explain what’s happening.

  • The src and dest functions are imported from Gulp. src will read the file that you pass as an argument and return a node stream.
  • We pull in yargs to create our flag that separates tasks between the development and production modes.
  • The three plugins are called into action.
  • The PRODUCTION flag is defined and held in the prod command.
  • We define styles as the task name we will use to run these tasks in the command line.
  • We tell the task what file we want processed (bundle.scss) and where it lives (src/scss/bundle.scss).
  • We create "pipes" that serve as the plungs that run when the styles command is executed. Those pipes run in the order they are written: convert Sass to CSS, minify the CSS (if we’re in production), and place the resulting CSS file into the dist/css directory.

Go ahead. Run gulp styles in the command line and see that a new CSS file has been added to your CSS directory dist/css.

Now do gulp styles --prod. The same thing happens, but now that CSS file has been minified for production use.

Now, assuming you have a functioning WordPress theme with header.php and footer.php, the CSS file (as well as JavaScript files when we get to those tasks) can be safely enqueued, likely in your functions.php file:

function _themename_assets() { wp_enqueue_style( '_themename-stylesheet', get_template_directory_uri() . '/dist/css/bundle.css', array(), '1.0.0', 'all' ); } add_action('wp_enqueue_scripts', '_themename_assets');

That’s all good, but we can make our style command even better.

For example, try inspecting the body on the homepage with the WordPress theme active. The styles that we added should be there:

As you can see, it says that our style is coming from bundle.css, which is true. However, it would be much better if the name of the original SCSS file is displayed here instead for our development purposes — it makes it so much easier to locate code, particularly when we’re working with a ton of partials. This is where source maps come into play. That will detail the location of our styles in DevTools. To further illustrate this issue, let's also add some SCSS inside src/scss/components/slider.scss and then import this file in bundle.scss.

//src/scss/components/slider.scss body { background-color: aqua; } //src/scss/bundle.scss @import './components/slider.scss'; $colour: #f03; body { background-color: $colour; }

Run gulp styles again to recompile your files. Your inspector should then look like this:

The DevTools inspector will show that both styles are coming from bundle.css. But we would like it to show the original file instead (i.e bundle.scss and slider.scss). So let’s add that to our wish list of improvements before we get to the code.

The other thing we’ll want is vendor prefixing to be handled for us. There’s nothing worse than having to write and manage all of those on our own, and Autoprefixer is the tool that can do it for us.

And, in order for Autoprefixer to work its magic, we’ll need the PostCSS plugin.

OK, that adds up to three more plugins and tasks we need to run. Let’s install all three:

npm install --save-dev gulp-sourcemaps gulp-postcss autoprefixer

So gulp-sourcemaps will obviously be used for sourcemaps. gulp-postcss and autoprefixer will be used to add autoprefixing to our CSS. postcss is a famous plugin for transforming CSS files and autoprefixer is just a plugin for postcss. You can read more about the other things that you can do with postcss here.

Now at the very top let’s import our plugins into the Gulpfile:

import postcss from 'gulp-postcss'; import sourcemaps from 'gulp-sourcemaps'; import autoprefixer from 'autoprefixer';

And then let’s update the task to use these plugins:

export const styles = () => { return src('src/scss/bundle.scss') .pipe(gulpif(!PRODUCTION, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(gulpif(PRODUCTION, postcss([ autoprefixer ]))) .pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'}))) .pipe(gulpif(!PRODUCTION, sourcemaps.write())) .pipe(dest('dist/css')); }

To use the the sourcemaps plugin we have to follow some steps:

  1. First, we initialize the plugin using sourcemaps.init().
  2. Next, pipe all the plugins that you would like to map.
  3. Finally, Create the source map file by calling sourcemaps.write() just before writing the bundle to the destination.

Note that all the plugins piped between sourcemaps.init() and sourcemaps.write() should be compatible with gulp-sourcemaps. In our case, we are using sass(), postcss() and cleanCss() and all of them are compatible with sourcemaps.

Notice that we only run the Autoprefixer begind the production flag since there’s really no need for all those vendor prefixes during development.

Let’s run gulp styles now, without the production flag. Here’s the output in bundle.css:

body { background-color: aqua; } body { background-color: #f03; } /*#sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmNzcyIsInNvdXJjZXMiOlsiYnVuZGxlLnNjc3MiLCJjb21wb25lbnRzL3NsaWRlci5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgJy4vY29tcG9uZW50cy9zbGlkZXIuc2Nzcyc7XG5cbiRjb2xvdXI6ICNmMDM7XG5ib2R5IHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAkY29sb3VyO1xufVxuOjpwbGFjZWhvbGRlciB7XG4gICAgY29sb3I6IGdyYXk7XG59IiwiYm9keSB7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogYXF1YTtcbn0iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFDQUEsQUFBQSxJQUFJLENBQUM7RUFDRCxnQkFBZ0IsRUFBRSxJQUFJLEdBQ3pCOztBRENELEFBQUEsSUFBSSxDQUFDO0VBQ0QsZ0JBQWdCLEVBRlgsSUFBSSxHQUdaOztBQUNELEFBQUEsYUFBYSxDQUFDO0VBQ1YsS0FBSyxFQUFFLElBQUksR0FDZCJ9 */#

The extra text below is source maps. Now, when we inspect the site in DevTools, we see:

Nice! Now onto production mode:

gulp styles --prod

Check DevTools against style rules that require prefixing (e.g. display: grid;) and confirm those are all there. And make sure that your file is minified as well.

One final notice for this task. Let’s assume we want multiple CSS bundles: one for front-end styles and one for WordPress admin styles. We can create add a new admin.scss file in the src/scss directory and pass an array of paths in the Gulpfile:

export const styles = () => { return src(['src/scss/bundle.scss', 'src/scss/admin.scss']) .pipe(gulpif(!PRODUCTION, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(gulpif(PRODUCTION, postcss([ autoprefixer ]))) .pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'}))) .pipe(gulpif(!PRODUCTION, sourcemaps.write())) .pipe(dest('dist/css')); }

Now we have bundle.css and admin.css in the dist/css directory. Just make sure to properly enqueue any new bundles that are separated out like this.

Creating the watch task

Alright, next up is the watch task, which makes our life so much easier by looking for files with saved changes, then executing tasks on our behalf without have to call them ourselves in the command line. How great is that?

Like we did for the styles task:

import { src, dest, watch } from 'gulp';

We’ll call the new task watchForChanges:

export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); }

Note that watch is unavailable as a name since we already have a variable using it.

Now let’s run gulp watchForChanges the command line will be on a constant, ongoing watch for changes in any .scss files inside the src/scss directory. And, when those changes happen, the styles task will run right away with no further action on our part.

Note that src/scss/**/*.scss is a glob pattern. That basically means that this string will match any .scss file inside the src/scss directory or any sub-folder in it. Right now, we are only watching for .scss files and running the styles task. Later, we’ll expand its scope to watch for other files as well.

Creating the images task

As we covered earlier, the images task will compress images in src/images and then move them to dist/images. Let’s install a gulp plugin that will be responsible for compressing images:

npm install --save-dev gulp-imagemin

Now, import this plugin at the top of the Gulpfile:

import imagemin from 'gulp-imagemin';

And finally, let’s write our images task:

export const images = () => { return src('src/images/**/*.{jpg,jpeg,png,svg,gif}') .pipe(gulpif(PRODUCTION, imagemin())) .pipe(dest('dist/images')); }

We give the src() function a glob that matches all .jpg, .jpeg, .png, .svg and .gif images in the src/images directory. Then, we run the imagemin plugin, but only for production. Compressing images can take some time and isn’t necessary during development, so we can leave it out of the development flow. Finally, we put the compressed versions of images in dist/images.

Now any images that we drop into src/images will be copied when we run gulp images. However, running gulp images --prod, will both compress and copy the image over.

Last thing we need to do is modify our watchForChanges task to include images in its watch:

export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', images); }

Now, assuming the watchForChanges task is running, the images task will be run automatically whenever we add an image to the src/images folder. It does all the lifting for us!

Important: If the watchForChanges task is running and when the Gulpfile is modified, it will need to be stopped and restarted in order for the changes to take effect.

Creating the copy task

You probably have been in situations where you’ve created files, processed them, then needed to manually grab the production files and put them where they need to be. Well, as we saw in the images task, we can use the copy feature to do this for us and help prevent moving wrong files.

export const copy = () => { return src(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*']) .pipe(dest('dist')); }

Try to read the array of paths supplied to src() carefully. We are telling Gulp to match all files and folders inside src (src/**/*), except the images, js and scss folders (!src/{images,js,scss}) and any of the files or sub-folders inside them (!src/{images,js,scss}/**/*).

We want our watch task to look for these changes as well, so we’ll add it to the mix:

export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', images); watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], copy); }

Try adding any file or folder to the src directory and it should be copied over to the the /dist directory. If, however, we were to add a file or folder inside of /images, /js or /scss, it would be ignored since we already handle these folders in separate tasks.

We still have a problem here though. Try to delete the added file and it won’t happen. Our task only handles copying. This problem could also happen for our /images, /js and /scss, folders. If we have old images or JavaScript and CSS bundles that were removed from the src folder, then they won’t get removed from the dist folder. Therefore, it’s a good idea to completely clean the dist folder every time to start developing or building a theme. And that’s what we are going to do in the next task.

Composing tasks for developing and building

Let’s now install a package that will be responsible for deleting the dist folder. This package is called del:

npm install --save-dev del

Import it at the top:

import del from 'del';

Create a task that will delete the dist folder:

export const clean = () => { return del(['dist']); }

Notice that del returns a promise. Thus, we don’t have to call the cb() function. Using the new JavaScript features allows us to refactor this to:

export const clean = () => del(['dist']);

The folder should be deleted now when running gulp clean. What we need to do next is delete the dist folder, run the images, copy and styles tasks, and finally watch for changes every time we start developing. This can be done by running gulp clean, gulp images, gulp styles, gulp copy and then gulp watch. But, of course, we will not do that manually. Gulp has a couple of functions that will help us compose tasks. So, let’s import these functions from Gulp:

import { src, dest, watch, series, parallel } from 'gulp';

series() will take some tasks as arguments and run them in series (one after another). And parallel() will take tasks as arguments and run them all at once. Let’s create two new tasks by composing the tasks that we already created:

export const dev = series(clean, parallel(styles, images, copy), watchForChanges) export const build = series(clean, parallel(styles, images, copy)) export default dev;

Both tasks will do the exact same thing: clean the dist folder, then styles, images and copy will run in parallel one the cleaning is complete. We will start watching for changes as well for the dev (short for develop) task, after these parallel tasks. Additionally, we are also exporting dev as the default task.

Notice that when we run the build task, we want our files to be minified, images to be compressed, and so on. So, when we run this command, we will have to add the --prod flag. Since this can easily be forgotten when running the build task, we can use npm scripts to create aliases for the dev and build commands. Let’s go to package.json, and in the scripts field, we will probably find something like this:

"scripts": { "test": "echo "Error: no test specified" && exit 1" }

Let’s change it to this:

"scripts": { "start": "gulp", "build": "gulp build --prod" },

This will allow us to run npm run start in the command line, which will go to the scripts field and find what command corresponds to start. In our case, start will run gulp and gulp will run the default gulp task, which is dev. Similarly, npm run build will run gulp build --prod. This way, we can completely forget about the --prod flag and also forget about running the Gulp tasks using the gulp command. Of course, our dev and build commands will do more than that later on, but for now, we have the foundation that we will work with throughout the rest of the tasks.

Creating the scripts task

As mentioned, in order to bundle our JavaScript files, we are going to need a module bundler. webpack is the most famous option out there, however it is not a Gulp plugin. Rather, it’s a plugin on its own that has a completely separate setup and configuration file. Luckily, there is a package called webpack-stream that helps us use webpack within a Gulp task. So, let’s install this package:

npm install --save-dev webpack-stream

webpack works with something called loaders. Loaders are responsible for transforming files in webpack. And to transform new Javascript versions into ES5, we will need a loader called babel-loader. We will also need @babel/preset-env but we already installed this earlier:

npm install --save-dev babel-loader

Let’s import webpack-stream at the top of the Gulpfile:

import webpack from 'webpack-stream';

Also, to test our task, lets add these lines in src/js/bundle.js and src/js/components/slider.js:

// bundle.js import './components/slider'; console.log('bundle'); // slider.js console.log('slider')

Our scripts task will finally look like so:

export const scripts = () => { return src('src/js/bundle.js') .pipe(webpack({ module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [] } } } ] }, mode: PRODUCTION ? 'production' : 'development', devtool: !PRODUCTION ? 'inline-source-map' : false, output: { filename: 'bundle.js' }, })) .pipe(dest('dist/js')); }

Let’s break this down a bit:

  • First, we specify bundle.js as our entry point in the src() function.
  • Then, we pipe the webpack plugin and specify some options for it.
  • The rules field in the module option lets webpack know what loaders to use in order to transform our files. In our case we need to transform JavaScript files using the babel-loader.
  • The mode option is either production or development. For development, webpack will not minify the output JavaScript bundle, but it will for production. Therefore, we don’t need a separate Gulp plugin to minify JavaScript because webpack can do that depending on our PRODUCTION constant.
  • The devtool option will add source maps, but not in production. In development, however, we will use inline-source-maps. This kind of source maps is the most accurate though it can be a bit slow to create. If you find it too slow, check the other options here. They won’t be as accurate as inline-source-maps but they can be pretty fast.
  • Finally, the output option can specify some information about the output file. In our case, we only need to change the filename. If we don’t specify the filename, webpack will generate a bundle with a hash as the filename. Read more about these options here.

Now we should be able to run gulp scripts and gulp scripts --prod and see a bundle.js file created in dist/js. Make sure that minification and source maps are working properly. Let’s now enqueue our JavaScript file in WordPress, which can be in the theme’s functions.php file, or wherever you write your functions.

<?php function _themename_assets() { wp_enqueue_style( '_themename-stylesheet', get_template_directory_uri() . '/dist/css/bundle.css', array(), '1.0.0', 'all' ); wp_enqueue_script( '_themename-scripts', get_template_directory_uri() . '/dist/js/bundle.js', array(), '1.0.0', true ); } add_action('wp_enqueue_scripts', '_themename_assets');

Now, looking at the console, let’s confirm that source maps are working correctly by checking the file that the console logs come from:

Without the source maps, both logs will appear coming from bundle.js.

What if we would like to create multiple JavaScript bundles the same way we do for the styles? Let’s create a file called admin.js in src/js. You might think that we can simply change the entry point in the src() to an array like so:

export const scripts = () => { return src(['src/js/bundle.js','src/js/admin.js']) . . }

However, this will not work. webpack works a bit differently that normal Gulp plugins. What we did above will still create one file called bundle.js in the dist folder. webpack-stream provides a couple of solutions for creating multiple entry points. I chose to use the second solution since it will allow us to create multiple bundles by passing an array to the src() the same way we did for the styles. This will require us to install vinyl-named:

npm install --save-dev vinyl-named

Import it:

import named from 'vinyl-named';

...and then update the scripts task:

export const scripts = () => { return src(['src/js/bundle.js','src/js/admin.js']) .pipe(named()) .pipe(webpack({ module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] }, mode: PRODUCTION ? 'production' : 'development', devtool: !PRODUCTION ? 'inline-source-map' : false, output: { filename: '[name].js' }, })) .pipe(dest('dist/js')); }

The only difference is that we now have an array in the src(). We then pipe the named plugin before webpack, which allows us to use a [name] placeholder in the output field’s filename instead of hardcoding the file name directly. After running the task, we get two bundles in dist/js.

Another feature that webpack provides is using libraries from external sources rather than bundling them into the final bundle. For example, let’s say your bundle needs to use jQuery. You can run npm install jquery --save and then import it to your bundle import $ from 'jquery'. However, this will increase the bundle size and, in some cases, you may already have jQuery loaded via a CDN or — in case of WordPress — it can exist as a dependency like so:

wp_enqueue_script( '_themename-scripts', get_template_directory_uri() . '/dist/js/bundle.js', array('jquery'), '1.0.0', true );

So, now WordPress will enqueue jQuery using a normal script tag. How can we then use it inside our bundle using import $ from 'jquery'? The answer is by using webpack’s externals option. Let’s modify our scripts task to add it in:

export const scripts = () => { return src(['src/js/bundle.js','src/js/admin.js']) .pipe(named()) .pipe(webpack({ module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [] } } } ] }, mode: PRODUCTION ? 'production' : 'development', devtool: !PRODUCTION ? 'inline-source-map' : false, output: { filename: '[name].js' }, externals: { jquery: 'jQuery' }, })) .pipe(dest('dist/js')); }

In the externals option, jquery is the key that identifies the name of the library we want to import. In our case, it will be import $ from 'jquery'. And the value jQuery is the name of a global variable where that the library lives. Now try to import $ from ‘jquery’ in the bundle and use jQuery using the $ — it should work perfectly.

Let’s watch for changes for JavaScript files as well:

export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', images); watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], copy); watch('src/js/**/*.js', scripts); }

And, finally, add our scripts task in the dev and build tasks:

export const dev = series(clean, parallel(styles, images, copy, scripts), watchForChanges); export const build = series(clean, parallel(styles, images, copy, scripts)); Refreshing the browser with Browsersync

Let’s now improve our watch task by installing Browsersync, a plugin that refreshes the browser each time tasks finish running.

npm install browser-sync gulp --save-dev

As usual, let’s import it:

import browserSync from "browser-sync";

Next, we will initialize a Browsersync server and write two new tasks:

const server = browserSync.create(); export const serve = done => { server.init({ proxy: "http://localhost/yourFolderName" // put your local website link here }); done(); }; export const reload = done => { server.reload(); done(); };

In order to control the browser using Browsersync, we have to initialize a Browsersync server. This is different from a local server where WordPresss would typically live. the first task is serve, which starts the Browsersync server, and is pointed to our local WordPress server using the proxy option. The second task will simply reload the browser.

Now we need to run this server when we are developing our theme. We can add the serve task to the dev series tasks:

export const dev = series(clean, parallel(styles, images, copy, scripts), serve, watchForChanges);

Now run npm start and the browser should open up a new URL that’s different than the original one. This URL is the one that Browsersync will refresh. Now let’s use the reload task to reload the browser once tasks are done:

export const watchForChanges = () => { watch('src/scss/**/*.scss', series(styles, reload)); watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', series(images, reload)); watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], series(copy, reload)); watch('src/js/**/*.js', series(scripts, reload)); watch("**/*.php", reload); }

As you can see, we added a new line to run the reload task every time a PHP file changes. We are also using series() to wait for our styles, images, scripts and copy tasks to finish before reloading the browser. Now, run npm start and change something in a Sass file. The browser should reload automatically and changes should be reflected after refresh once the tasks have finished running.

Don’t see CSS or JavaScript changes after refresh? Make sure caching is disabled in your browser’s inspector.

We can make even one more improvement to the styles tasks. Browsersync allows us to inject CSS directly to the page without even having to reload the browser. And this can be done by adding server.stream() at the very end of the styles task:

export const styles = () => { return src(['src/scss/bundle.scss', 'src/scss/admin.scss']) .pipe(gulpif(!PRODUCTION, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(gulpif(PRODUCTION, postcss([ autoprefixer ]))) .pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'}))) .pipe(gulpif(!PRODUCTION, sourcemaps.write())) .pipe(dest('dist/css')) .pipe(server.stream()); }

Now, in the watchForChanges task, we won’t have to reload for the styles task any more, so let’s remove the reload task from it:

export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); . . }

Make sure to stop watchForChanges if it’s already running and then run it again. Try to modify any file in the scss folder and the changes should appear immediately in the browser without even reloading.

Packaging the theme in a ZIP file

WordPress themes are generally packaged up as a ZIP file that can be installed directly in the WordPress admin. We can create a task that will take the required theme files and ZIP them up for us. To do that we need to install another Gulp plugin: gulp-zip.

npm install --save-dev gulp-zip

And, as always, import it at the top:

import zip from "gulp-zip";

Let’s also import the JSON object in the package.json file. We need that in order to grab the name of the package which is also the name of our theme:

import info from "./package.json";

Now, let’s write our task:

export const compress = () => { return src([ "**/*", "!node_modules{,/**}", "!bundled{,/**}", "!src{,/**}", "!.babelrc", "!.gitignore", "!gulpfile.babel.js", "!package.json", "!package-lock.json", ]) .pipe(zip(`${info.name}.zip`)) .pipe(dest('bundled')); };

We are passing the src() the files and folders that we need to compress, which is basically all files and folders (**/), except a few specific types of files, which are preceded by !. Next, we are piping the gulp-zip plugin and calling the file the name of the theme from the package.json file (info.name). The result is a fresh ZIP file an a new folder called bundled.

Try running gulp compress and make sure it all works. Open up the generated ZIP file and make sure that it only contains the files and folders needed to run the theme.

Normally, though, we only need to ZIP things up *after* the theme files have been built. So let’s add the compress task to the build task so it only runs when we need it:

export const build = series(clean, parallel(styles, images, copy, scripts), compress);

Running npm run build should now run all of our tasks in production mode.

Replacing the placeholder prefix in the ZIP file

One step we need to do before zipping our files is to scan them and replace the themename placeholder with the theme name we plan to use. As you may have guessed, there is indeed a Gulp plugin that does that for us, called gulp-replace.

npm install --save-dev gulp-replace

Then import it:

import replace from "gulp-replace";

We want this task to run immediately before our files are zipped, so let’s modify the compress task by slotting it in the right place:

export const compress = () => { return src([ "**/*", "!node_modules{,/**}", "!bundled{,/**}", "!src{,/**}", "!.babelrc", "!.gitignore", "!gulpfile.babel.js", "!package.json", "!package-lock.json", ]) .pipe(replace("_themename", info.name)) .pipe(zip(`${info.name}.zip`)) .pipe(dest('bundled')); };

Try to building the theme now with npm run build and then unzip the file inside the bundled folder. Open any PHP file where the _themename placeholder may have been used and make sure it’s replaced with the actual theme name.

There is a gotcha to watch for that I noticed in the replace plugin as I was working with it. If there are ZIP files inside the theme (e.g. you are bundling WordPress plugins inside your theme), then they will get corrupted when they pass through the replace plugin. That can be resolved by ignoring ZIP files using a gulp-if statement:

.pipe( gulpif( file => file.relative.split(".").pop() !== "zip", replace("_themename", info.name) ) ) Generating a POT file

Translation is a big thing in the WordPress community, so for our final task, we let’s scan through all of our PHP files and generate a POT file that gets used for translation. Luckily, we also have a gulp plugin for that:

npm install --save-dev gulp-wp-pot

And, of course, import it:

import wpPot from "gulp-wp-pot";

Here’s our final task:

export const pot = () => { return src("**/*.php") .pipe( wpPot({ domain: "_themename", package: info.name }) ) .pipe(gulp.dest(`languages/${info.name}.pot`)); };

We want the POT file to generate every time we build the theme:

export const build = series(clean, parallel(styles, images, copy, scripts), pot, compress); Summing up

Here’s the complete Gulpfile, including all of the tasks we covered in this post:

import { src, dest, watch, series, parallel } from 'gulp'; import yargs from 'yargs'; import sass from 'gulp-sass'; import cleanCss from 'gulp-clean-css'; import gulpif from 'gulp-if'; import postcss from 'gulp-postcss'; import sourcemaps from 'gulp-sourcemaps'; import autoprefixer from 'autoprefixer'; import imagemin from 'gulp-imagemin'; import del from 'del'; import webpack from 'webpack-stream'; import named from 'vinyl-named'; import browserSync from "browser-sync"; import zip from "gulp-zip"; import info from "./package.json"; import replace from "gulp-replace"; import wpPot from "gulp-wp-pot"; const PRODUCTION = yargs.argv.prod; const server = browserSync.create(); export const serve = done => { server.init({ proxy: "http://localhost:8888/starter" }); done(); }; export const reload = done => { server.reload(); done(); }; export const clean = () => del(['dist']); export const styles = () => { return src(['src/scss/bundle.scss', 'src/scss/admin.scss']) .pipe(gulpif(!PRODUCTION, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(gulpif(PRODUCTION, postcss([ autoprefixer ]))) .pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'}))) .pipe(gulpif(!PRODUCTION, sourcemaps.write())) .pipe(dest('dist/css')) .pipe(server.stream()); } export const images = () => { return src('src/images/**/*.{jpg,jpeg,png,svg,gif}') .pipe(gulpif(PRODUCTION, imagemin())) .pipe(dest('dist/images')); } export const copy = () => { return src(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*']) .pipe(dest('dist')); } export const scripts = () => { return src(['src/js/bundle.js','src/js/admin.js']) .pipe(named()) .pipe(webpack({ module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [] } } } ] }, mode: PRODUCTION ? 'production' : 'development', devtool: !PRODUCTION ? 'inline-source-map' : false, output: { filename: '[name].js' }, externals: { jquery: 'jQuery' }, })) .pipe(dest('dist/js')); } export const compress = () => { return src([ "**/*", "!node_modules{,/**}", "!bundled{,/**}", "!src{,/**}", "!.babelrc", "!.gitignore", "!gulpfile.babel.js", "!package.json", "!package-lock.json", ]) .pipe( gulpif( file => file.relative.split(".").pop() !== "zip", replace("_themename", info.name) ) ) .pipe(zip(`${info.name}.zip`)) .pipe(dest('bundled')); }; export const pot = () => { return src("**/*.php") .pipe( wpPot({ domain: "_themename", package: info.name }) ) .pipe(dest(`languages/${info.name}.pot`)); }; export const watchForChanges = () => { watch('src/scss/**/*.scss', styles); watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', series(images, reload)); watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], series(copy, reload)); watch('src/js/**/*.js', series(scripts, reload)); watch("**/*.php", reload); } export const dev = series(clean, parallel(styles, images, copy, scripts), serve, watchForChanges); export const build = series(clean, parallel(styles, images, copy, scripts), pot, compress); export default dev;

Phew, that’s everything! I hope you learned something from this series and that it helps streamline your WordPress development flow. Let me know if you have any questions in the comments. If you are interested in a complete WordPress theme development course, make sure to check out my course on Udemy with a special discount for you. &#x1f600;

The post Gulp for WordPress: Creating the Tasks appeared first on CSS-Tricks.

Gulp for WordPress: Initial Setup

Css Tricks - Wed, 12/26/2018 - 5:04am

This is the first part of a two-part series on creating a Gulp workflow for WordPress theme development. This first part covers a lot of ground for the initial setup, including Gulp installation and an outline of the tasks we want it to run. If you're interested in how the tasks are created, then stay tuned for part two.

Earlier this year, I created a course for building premium WordPress themes. During the process, I wanted to use a task runner to concatenate and minify JavaScript and CSS files. I ended up using that task runner to automate a lot of other tasks that made the theme much more efficient and scalable.

The two most popular task runners powered by Node are Gulp and Grunt. I went with Gulp after a good amount of research, it appeared to have an intuitive way to write tasks. It uses Node streams to manipulate files and JavaScript functions to write the tasks, whereas Grunt uses a configuration object to define tasks — which might be fine for some, but is something that made me a little uncomfortable. Also, Gulp is a bit faster than Grunt because of those Node streams and faster is always a good thing to me!

So, we're going to set Gulp up to do a lot of the heavy lifting for WordPress theme development. We'll cover the initial setup for now, but then go super in-depth on the tasks themselves in another post.

Article Series:
  1. Initial Setup (This Post)
  2. Creating the Tasks
Initial theme setup

So, how can we use Gulp to power the tasks for a WordPress theme? First off, let’s assume our theme only contains the two files that WordPress requires for any theme: index.php and styles.css. Sure, most themes are likely to include many more files that this, but that’s not important right now.

Secondly, let’s assume that our primary goal is to create tasks that help manage our assets, like minify our CSS and JavaScript files, compile Sass to CSS, and transpile modern JavaScript syntax (e.g. ES6, ES7, etc..) into ES5 in order to support older browsers.

Our theme folder structure will look like this:

themefolder/ ??? index.php ??? style.css ??? src/ ??? images/ ? ??? cat.jpg ??? js/ ? ??? components/ ? ? ??? slider.js ? ??? bundle.js ??? scss/ ??? components/ ? ??? slider.scss ??? bundle.scss

The only thing we’ve added on top of the two required files is a src directory where our original un-compiled assets will live.

Inside of that src directory, we have an images subdirectory as well as others for our JavaScript and Sass files. And from, there, the JavaScript and Sass subdirectories are organized into components that will be called from their respective bundle file. So, for example, bundle.js will import and include slider.js when our JavaScript tasks run so all our code is concatenated into a single file.

Identifying Gulp tasks

OK, next we want Gulp tasks to a create a new dist directory where all of our compiled, minified and concatenated versions of our assets will be distributed after the tasks have completed. Even though we’re calling this directory dist in this post because it is short for "distribution," it could really be called anything, as long as Gulp knows what it is called. Regardless, these are the assets that will be distributed to end users. The src folder will only contain the files that we edit directly during development.

Identifying which Gulp tasks are the best fit for a project will depend on the project’s specific needs. Some tasks will be super helpful on some projects but completely irrelevant on others. I’ve identified the following for us to cover in this post. You’ll see that one or two are more useful in a WordPress context (e.g. the POT task) than others. Yet, most of these are broad enough that you’re likely to see them in many projects that use Gulp to process Sass, JavaScript and image assets.

  • Styles Task: This task is responsible for compiling the bundle.scss file in the scss subdirectory to bundle.css in a css directory located in the dist directory. This task will also minify the generated CSS file so that its is it’s smallest possible size when used in production.

We will talk about production vs. development modes during the article. Note that we will not create a task to concatenate CSS files. The bundle.scss file will act as an entry point for all . <code>scss files that we want to include. In other words; any Sass or CSS files you want to include in your project, just import them in the bundle.scss file using @import statements. For instance, in our example folder, we can use @import ./components/slider'; to import the slider.scss file. This way in our task we will have to compile and minify only one file (bundle.css).

  • Scripts Task: Similar to the Styles task, this task will transpile bundle.js from ES6 syntax to ES5, then minify the file for production.

We will only compile bundle.js. Any other JavaScript files we want to include will be done using ES6 import statements. But in order for those import statements to work on all browsers, we will need to use a module bundler. We’re going to use webpack as our bundler. If this is your first time working with it, this primer is a good place to get an overview of what it is and does.

  • Images Task: This task will simply copy images from src/images and send them to dist/images after the files have been compressed to their smallest size.
  • Copy Task: This task will be responsible for copying any other files or folders that are not in /src/images, /src/js or /src/scss and post them to the dist directory.

Remember. the src folder will contain the files that are only used during development and that will not be included in the final theme package. Thus, any assets other than our images, JavaScript and Sass files need to be copied posted to the dist folder. For instance, if we have a /src/fonts folder, we would want to copy the files in there into the dist directory so they get included in the final deliverable.

  • POT Task: As the name suggests, this task will scan all your theme’s PHP files and generate a .pot (i.e. translation) file from gettext calls in the files. This is the most WordPress-centric of all the tasks we’re covering here.
  • Watch Task: This task will literally watch for changes in your files. When a change is made, certain tasks will be triggered and executed, depending on the type of file that changed.

For instance, if we change a JavaScript file, then the Scripts task should do its magic and then it would be ideal if the browser refreshed for us automatically so we can see those changes. Further, If we change a PHP file, then let’s simply refresh the browser since PHP files don’t rely on any other tasks in our project. We’ll be using a Gulp plugin called Browsersync to handle browser refreshes, but we’ll get to that and other plugins a little later.

  • Compress Task: As you might expect, all the plugins that we use to write our tasks will be managed using npm. So, our theme folder will contain another folder, like node_modules, that in turn, contains files like package.json and other configuration files that define our project’s dependencies — and these files and folders are only needed during development. During production, we can take out the necessary files for our theme and leave the unneeded development files behind. That’s what this task will do; it will create a ZIP file that only contains the necessary files to run our theme.

As a bonus step for the compress task, if you are creating a theme that you intend to publish on WordPress.org or maybe on a website like ThemeForest, then you probably already know that all functions in your theme must be prefixed with a unique prefix:

function mythemename_enqueue_assets() { // function body }

So, if you are creating a lot of themes. you'll need to easily reuse functions in different themes but change the prefix to the name of the theme, to prevent conflicts. We can prefix our functions with a placeholder prefix and then replace all instances of that placeholder in the compress task. For instance, we can choose the string _themename as a place holder, and when we compress our theme we will replace all ‘_themename’ strings to the actual theme name:

function _themename_enqueue_assets() { // function body }

This can also apply to anywhere we use the theme name for example in the text domain:

<?php _e('some string', '_themename'); ?>
  • Develop Task: This task does nothing new. It runs as we develop our theme. It cleans the dist folder, runs the Styles, Scripts, Images and Copy tasks in development mode (i.e. without minifying any of the assets), then watches for file changes to refresh the browser for us.
  • Build Task: This task is intended to build our files for production. It will do all the same cleaning and tasks as the Develop task, but in production mode (i.e. minify the assets in the process) and generate a new POT file for translation updates. After it runs, our dist folder should contain the files that are ready for distribution.
  • Bundle Task: This task will simply run the build task, making sure that all the files in the dist folder are minified and ready for distribution. Then, it will run the Compress task, which bundles all of the production-ready files and folders into a ZIP file. We want a ZIP file because that is the format WordPress recognizes to extract and install a theme.

Here’s how our file structure looks after our tasks complete:

themefolder/ ??? index.php ??? style.css ??? src/ ??? dist/ ??? images/ ? ??? cat.jpg // after compression ??? js/ ? ??? bundle.js // bundled with all imported files (minified in production) ??? scss/ ??? bundle.scss // bundled with all imported files (minified in production)

Now that we know what tasks we’re going to use on our project and what they do, let’s get into the process for installing Gulp into the project.

Installing Gulp

Before we install Gulp, we should make sure that we have Node and npm installed on our machines. We can do that by running these commands in the command line:

node --version npm --version

...and, we should get some version number as seen here:

Now, let’s point the command line to the theme folder:

cd path/to/your/theme/folder

...and then run this command to initialize a new npm project:

npm init

This will prompt us with some options. The only important option in our case is the package name option. This is where the name of the theme can be provided — everything else can stay at their default setting. When choosing the theme name, make sure to only use lowercase characters and underscores while avoiding dashes and special characters since this theme name will be used to replace the functions placeholder that we mentioned earlier.

On to installing Gulp! First, we’ve got to install Gulp’s command line interface (gulp-cli) globally so we can use Gulp in the command line.

npm install --global gulp-cli

After that, we run this command in order to install Gulp itself in the theme directory:

npm install –save-dev gulp

The current stable release of Gulp is 3.9.1 at the time of this writing, but version 4.0 is already available in the project repository.

To make sure everything is installed correctly, we’ll run this command:

gulp --version

Nice! Looks like we’re running version 4.0, which is the latest version at the time of this writing.

Writing Gulp tasks

Gulp tasks are defined in a a file in called gulpfile.js that we’ll need to create and place into the root of our theme.

Gulp is JavaScript at its core, so we can define a quick example task that logs something to the console.

var gulp = require('gulp'); gulp.task('hello', function() { console.log('First Task'); })

In this example, we’ve defined a new task by calling gulp.task. The first argument for this function is the task’s name (hello) and the second argument is the function we want to run when that name is entered into the command line which, in this case, should print "First Task" into the console.

Let’s do that now.

gulp hello

Here’s what we get:

As you can see, we do indeed get the console.log('First Task') output we want. However, we also get an error saying that our task did not complete. All Gulp tasks require telling Gulp where to end the task, and we do that by calling a function that is passed as the first argument in our task function like so:

var gulp = require('gulp'); gulp.task('hello', function(cb) { console.log('First Task'); cb(); })

Let’s try running gulp hello again and we should get the same output, but without the error this time.

cb() is a Node.js callback function that's often passed into an asynchronous function. There are some cases where we won’t have to call it, such as when a task returns a promise or a node stream. A node stream is what we will use in the tasks in this post, which means we will see it a lot throughout our article.

Here’s an example of a task that returns a promise. In this task, we won’t have to call the cb() function because Gulp already knows that the task will end when the promise resolves or returns an error:

gulp.task('promise', function(cb) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, 300); }); });

Now try and run ‘gulp promise’ and the task will complete without returning any errors.

Finally, it’s worth mentioning that Gulp accepts a default task that runs by typing gulp alone in the command line. All it takes is using "default" as the task name argument.

gulp.task('default', function(cb) { console.log('Default Task'); cb(); });

Now, typing gulp by itself in the command line will run the task.

Whew! Now we know the basics of writing Gulp tasks.

There is one more thing we can do to improve things and that’s enabling ES6 syntax in the Gulpfile. This will allow us to use features like destructuring, import statements, and arrow functions, among others.

Using ES6 in the Gulpfile

The first step to use ES6 syntax in the Gulpfile is to rename it from gulpfile.js to gulpfile.babel.js. As you may already know, Babel is the compiler that compiles ES6 to ES5.

So, let’s install Babel and some of its required packages by running:

npm install --save-dev @babel/register @babel/preset-env @babel/core

After that, we have to create a file called .babelrc inside of our theme folder. This file will tell Babel which preset to use to compile our JavaScript. The contents of the .babelrc file will look like this:

{ "presets": ["@babel/preset-env"] }

Now we can use ES6 in our Gulpfile! Here’s how that would look if we were to re-write it:

import gulp from 'gulp'; export const hello = (cb) => { console.log('First Task'); cb(); } export const promise = (cb) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 300); }); }; export default hello

As you can see, we are importing Gulp using import and not require. In fact, there’s no longer any need to import Gulp at all! I included the import statement anyway to show it can be used instead of require. We’re allowed to skip importing Gulp because we don’t have to call gulp.task — instead, we only need to export a function, and the name of this function will be the name of the task. Further, all that’s needed to define a default function is use export default. And notice those arrow functions, too! Everything is so much more concise.

Let’s move on and start coding the actual tasks.

Development vs. Production

As we covered earlier, we need to create two modes: development and production. The reason we need to delineate between the two is that some details in our tasks will be time and memory-consuming, which only make sense in a production environment. For instance, the styles task needs to minify the CSS. However, the minification can take both time and memory — and if that process runs every single time something changes during development, that is not only unnecessary, but is very inefficient. It’s ideal for tasks to be as fast as possible during development.

We need to set a flag that specifies whether a task should run in one mode or the other. We can use a package called yargs that allows us to define these types of arguments while running a command. So, let’s install it and put it to use:

npm install --save-dev yargs

Now, we can add arguments to our command like so:

gulp hello --prod=true

...and then retrieve these argument in the Gulpfile:

import yargs from 'yargs'; const PRODUCTION = yargs.argv.prod; export const hello = (cb) => { console.log(PRODUCTION); cb(); }

Notice that the values we define in the command are available inside the yargs.argv object in the Gulpfile and in console.log(PRODUCTION). In our case this will output true, so PRODUCTION will be our flag that decides whether or not a function runs inside the tasks.

We're all set up!

We covered a lot of ground here, but we now have everything we need to start writing tasks for our WordPress theme development. That just so happens to be the sole focus of the next part of this series, so stay tuned for tomorrow.

The post Gulp for WordPress: Initial Setup appeared first on CSS-Tricks.

An Initial Implementation of clip-path: path();

Css Tricks - Mon, 12/24/2018 - 6:40am

One thing that has long surprised (and saddened) me is that the clip-path property, as awesome as it is, only takes a few values. The circle() and ellipse() functions are nice, but hiding overflows and rounding with border-radius generally helps there already. Perhaps the most useful value is polygon() because it allows us to draw a shape out of straight lines at arbitrary points.

Here's a demo of each value:

See the Pen clip-path examples by Chris Coyier (@chriscoyier) on CodePen.

The sad part comes in when you find out that clip-path doesn't accept path(). C'mon it's got path in the name! The path syntax, which comes from SVG, is the ultimate syntax. It allows us to draw literally any shape.

More confusingly, there already is a path() function, which is what properties like offset-path take.

I was once so flabbergasted by all this that I turned it into a full conference talk.

The talk goes into the shape-outside property and how it can't use path(). It also goes into the fact that we can change the d property of a literal <path>.

I don't really blame anyone, though. This is weird stuff and it's being implemented by different teams, which inevitably results in different outcomes. Even the fact that SVG uses unit-less values in the <path> syntax is a little weird and an anomaly in CSS-land. How that behaves, how values with units behave, what comma-syntax is allowed and disallowed, and what the DOM returns when asked is plenty to make your head spin.

Anyway! Along comes Firefox with an implementation!

Does anyone know if clip-path: path() is behind a flag in chrome or something. Can't seem to find it, but they do support offset-path: path(), so figured they would support both.

Thanks @CSS and @ChromiumDev friends.https://t.co/YTmwcnilRB works behind a flag in FF. Chrome?

— Estelle Weyl (@estellevw) September 13, 2018

Here's that flag in Firefox (layout.css.clip-path-path.enabled):

And here's a demo... you'll see a square in unsupported browsers and a heart in the ones that support clip-path: path(); — which is only Firefox Nightly with the flag turned on at the time of this writing.

See the Pen clip-path: path()! by Chris Coyier (@chriscoyier) on CodePen.

Now, all we need is:

  • clip-path to be able to point to the URL of a <clipPath> in SVG, like url("#clip-path");
  • shape-outside to be able to use path()
  • shape-outside to be able to use a <clipPath>
  • offset-path to take all the other shape functions
  • Probably a bunch of specs to make sure this is all handled cleanly (Good luck, team!)
  • Browsers to implement it all

&#x1f609;

The post An Initial Implementation of clip-path: path(); appeared first on CSS-Tricks.

People Talkin’ Shapes

Css Tricks - Fri, 12/21/2018 - 1:56pm

Codrops has a very nice article on CSS Shapes from Tania Rascia. You might know shape-outside is for redefining the area by which text is floated around that element, allowing for some interesting design opportunities. But there are a couple of genuine CSS tricks in here:

  1. Float shape-outside elements both right and left to get text to flow between them.
  2. You can set shape-outside to take an image and use shape-image-threshold to adjust where the text flows, meaning you could even use a gradient!

Shapes are in the water recently, as Heydon Pickering recently published a short video on using them. He also covers things like clip-path and canvas and such:

We recently moved our long-time page on (basically faking) CSS shapes over to a blog post so it's easier to maintain.

Robin also wrote Working with Shapes in Web Design that digs into all this. So many tricks!

See the Pen 10c03204463e92a72a6756678e6348d1 by CSS-Tricks (@css-tricks) on CodePen.

When we talk about CSS shapes, it's almost like we're talking about values moreso than properties. What I mean is that the value functions like polygon(), circle(), ellipse(), offset(), path(), etc. are more representative of "CSS shapes" than the properties they are applied to. Multiple properties take them, like shape-outside, clip-path, and offset-path.

I once did a whole talk on this:

The only thing that's changed since then is that Firefox started allowing clip-path: path() behind the flag layout.css.clip-path-path.enabled (demo).

And don't forget Jen Simmons was talking about the possibilities of CSS Shapes (in her lab demos) years earlier!

The post People Talkin’ Shapes appeared first on CSS-Tricks.

Animating Between Views in React

Css Tricks - Fri, 12/21/2018 - 5:16am

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

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

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

Transitions using react-transition-group

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

Single components transitions

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

A dialog component might look something like this:

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

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

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

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

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

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

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

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

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

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

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

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

Routing

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

Let’s start with a basic route definition:

import React, { Component } from 'react' import { BrowserRouter, Switch, Route } from 'react-router-dom' import Home from '../views/Home' import Author from '../views/Author' import About from '../views/About' import Nav from '../components/Nav' class App extends Component { render() { return ( <BrowserRouter> <div className="app"> <Switch> <Route exact path="/" component={Home}/> <Route path="/author" component={Author} /> <Route path="/about" component={About} /> </Switch> </div> </BrowserRouter> ) } }

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

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

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

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

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

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

React keys to the rescue

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

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

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

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

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

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

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

Inside the animation function

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

export const play = (pathname, node, appears) => { const delay = appears ? 0 : 0.5 let timeline if (pathname === '/') timeline = getHomeTimeline(node, delay) else timeline = getDefaultTimeline(node, delay) timeline.play() }

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

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

const getHomeTimeline = (node, delay) => { const timeline = new Timeline({ paused: true }); const texts = node.querySelectorAll('h1 > div'); timeline .from(node, 0, { display: 'none', autoAlpha: 0, delay }) .staggerFrom(texts, 0.375, { autoAlpha: 0, x: -25, ease: Power1.easeOut }, 0.125); return timeline }

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

Success!

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

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

Profiling the initial load

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Wrapping up

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

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

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

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

Regarding CSS’s Global Scope

Css Tricks - Thu, 12/20/2018 - 4:45am
html { font-family: Roboto, sans-serif; }

With the except of some form elements, you've just set a font on every bit of text on a site! Nice! That's probably what you were trying to do, because of the probably hundreds of elements all over your site, setting that font-family every time would be tedious and error-prone.

CSS is global by nature. On purpose!

I like how David Khourshid put it:

You ever stop and think about why CSS has a global scope? Maybe we want to use consistent typography, colors, sizing, spacing, layout, transitions, etc. and have our websites & apps feel like one cohesive unit?

Love the cascade, the cascade is your friend.

And yet. The global nature of CSS is perhaps the most-pointed-at anti-feature of CSS. Some people really don't like it. We all know it's very easy to write a single CSS rule that has implications all over a site, breaking things you really didn't want to break.

Two CSS properties walk into a bar.

A barstool in a completely different bar falls over.

— Thomas Fuchs &#x1f384;&#x1f579;&#x1f4be; (@thomasfuchs) July 28, 2014

There are whole new categories of testing to assist with these problems.

Scoped styles aren't the only reason there is such interest and adoption in the landscape of tools that is CSS-in-JS, but it's a big one. There are loads of sites that don't directly author any CSS at all — even preprocessed styles — and go for a JavaScript library instead where styles are authored quite literally in JavaScript. There is a playground demonstrating the syntax of the various options. Here's how styled-components works:

import React from 'react'; import styled from 'styled-components'; const Container = styled.main` display: flex; flex-direction: column; min-height: 100%; width: 100%; background-color: #f6f9fc; `; export default function Login() { return ( <Container> ... Some stuff .... </Container> ); }

There are literally dozens of options, each doing things a bit differently while offering slightly different syntaxes and features. Vue even offers scoped CSS directly in .vue files:

<style scoped> .example { color: red; } </style> <template> <div class="example">hi</div> </template>

Unfortunately, <style scoped> never quite made it as a native web platform feature. There is shadow DOM, though, where a style block can be injected in a template and those styles will be isolated from the rest of the page:

let myElement = document.querySelector('.my-element'); let shadow = myElement.attachShadow({ mode: 'closed' }); shadow.innerHTML = ` <style> p { color: red; } </style> <p>Element with Shadow DOM</p> `;

No styles will leak into or out of that shadow DOM boundary. That's pretty cool for people seeking this kind of isolation, but it could be tricky. You'd likely have to architect the CSS to have certain global styles that can be imported with the shadow DOM'd web component so it can achieve some styling cohesion in your site. Personally, I wish it was possible to make the shadow DOM one-way permeable: styles can leak in, but styles defined inside can't leak out.

CSS-in-JS stuff is only one way to scope styles. There are actually two sides to the spectrum. You could call CSS-in-JS total isolation, whereas you could author CSS directly with total abstraction:

Total abstraction might come from a project, like Tachyons, that gives you a fixed set of class names to use for styling (Tailwind is like a configurable version of that), or a programmatic tool (like Atomizer) that turns specially named HTML class attributes into a stylesheet with exactly what it needs.

Even adhering 100% to BEM across your entire site could be considered total CSS isolation, solving the problems that the global scope may bring.

Personally, I'd like to see us move to this kind of future:

When we write styles, we will always make a choice. Is this a global style? Am I, on purpose, leaking this style across the entire site? Or, am I writing CSS that is specific to this component? CSS will be split in half between these two. Component-specific styles will be scoped and bundled with the component and used as needed.

Best of both worlds, that.

Anyway, it's tricky.

The problem is not CSS in JS.

It is CSS's global scope.

Solve the global scope, and CSS in JS will follow.

(I don't know if "follow" means disappear, being fully accepted, or getting a major overhaul.)

(For that matter, I don't know what "solving the global scope" means.)

— ppk &#x1f1ea;&#x1f1fa; (@ppk) November 28, 2018

Maybe this will be the hottest CSS topic in 2019.

The post Regarding CSS’s Global Scope appeared first on CSS-Tricks.

The Fragmented, But Evolving State of CSS-in-JS

Css Tricks - Thu, 12/20/2018 - 4:43am

TLDR: The CSS-in-JS community has converged on a consistent API.

Not so long ago, a Facebook engineer compiled a list of the available CSS-in-JS methodologies. It wasn’t short:

aphrodite, babel-plugin-css-in-js, babel-plugin-pre-style, bloody-react-styled, classy, csjs, css-constructor, css-light, css-loader, css-ns, cssobj, cssx-loader, cxs, electron-css, emotion, es-css-modules, freestyler, glamor, glamorous, hiccup-css, hyperstyles, i-css, j2c, jsxstyle, linaria, nano-css, pre-style, radium, react-css-builder, react-css-components, react-css-modules, react-cssom, react-fela, react-free-style, react-inline-css, react-inline-style, react-inline, react-jss, react-look, react-native-web, react-statics-styles, react-styl, react-style, react-styleable, react-stylematic, react-theme, react-vstyle, reactcss, restyles, scope-styles, smart-css, stile-react-media-queries, stilr, stylable, style-it, styled-components, styled-jsx, styletron-react, styling, superstyle, typestyle, uranium

Such a fragmented ecosystem was far from appealing. Which one should you pick, (if any)?

Contributing to Javascript fatigue — you need at most one. Also feel free to not learn any.

GitHub stars are one useful metric:

However, GitHub stars say nothing about a project’s trajectory — perhaps they were accumulated long ago and the repo has since fallen out of favor or is no longer maintained. Glamor has plenty of open issues, and hasn’t seen a commit in over a year. Its author advises:

...it mostly works, I'm not going to do any major changes… if you need something more modern, I'd recommend emotion, it mostly matches glamor's api, and is actively maintained.

The similarly named Glamorous was recently deprecated with its author also recommending users switch to Emotion:

At the time, Emotion had some features that Styled Components didn’t. Since then, Styled Components has made some big announcements.

Styled Components sells itself as the CSS-in-JS library for people that *like* CSS. Styled Components gained popularity by utilizing tagged template literals — allowing developers to *just write CSS* in the same syntax they already know, but inside JavaScript files. While this has proven popular, some developers prefer to [write styles as JavaScript objects. Emotion offered flexibility — developers could choose how to write their styles. Styled Components eventually followed suit.

styled-components v3.3.0 is out with first-class object support! &#x1f60d;

Lots of people have been asking for this, your wishes have been heard! Shoutout to @probablyup for taking care of this release.

&#x1f449; https://t.co/yOHWg78nF4 pic.twitter.com/Ic8fZdAFVs

— Max Stoiber (@mxstbr) May 25, 2018

Emotion also offers a css prop, which Styled Components didn’t have, until…

&#x1f389; Announcing support for the css prop in styled-components! &#x1f389;

This has been a long time coming, hope y'all enjoy! ✨

&#x1f449; https://t.co/DMdrG6uviZ

Huge shoutout to @satya164 for coming up with the ingenious implementation! &#x1f44f;

— Max Stoiber (@mxstbr) November 26, 2018

The rival CSS-in-JS libraries have stolen from each other until landing upon the same feature set and the same syntax — Emotion and Styled Components have an almost identical API. What once felt like a total mess of competing methodologies and libraries now feels somewhat stable. Even if CSS-in-JS hasn’t standardized on a dependency, it now has standardized a way of doing things — they’re just implemented differently:

Internally, quite a bit. SC has a lot of complexity around organizing style tag order.
Re css prop: SC requires Babel plugin and uses the entire SC custom component creation. Emotion will skip the custom component if it can and just renders the element with the className directly

— Kye Hohenberger (@tkh44) December 7, 2018

Styled Components is by far the most popular CSS-in-JS library, but Emotion has seen a rapid increase in usage.

Both are used by some major companies. Styled Components are utilized by plenty of large companies, including Bloomberg, Atlassian, Reddit, Target, BBC News, The Huffington Post, Coinbase, Patreon, Vogue, Ticketmaster, Lego, InVision and Autodesk just to name a few.

Emotion boasts fewer recognizable names, but has been recently adopted by the New York Times.

Great article about the launch of our new Story designs on the NYT today. It mentions our Shared Components initiative - would have been impossible without Emotion / CSS-in-JS. Absolute game-changer. Living in the future. https://t.co/pZLDJjsbEr

— Scott Taylor (@wonderboymusic) May 8, 2018

While these libraries certainly do seem to be most popular amongst React users, they can be used with other frameworks. While they seem to have converged on the same features at last, it’s difficult to say whether this is the end point of CSS-in-JS, or whether we’ll see a continued evolution from here.

The post The Fragmented, But Evolving State of CSS-in-JS appeared first on CSS-Tricks.

WooCommerce

Css Tricks - Thu, 12/20/2018 - 4:27am

(This is a sponsored post.)

I just read a nicely put together story about WooCommerce over on the CodeinWP blog. WooCommerce started life as WooThemes, sort of a "premium themes" business started by just a couple of fellas who had never even met in person. Two years and a few employees later they launch WooCommerce, and 2 years after that it hits a million downloads. A major success story, to be sure, but a collaborative and remote-work based one that wasn't exactly overnight. Another 2 years and Automattic picks them up and the WooThemes part is spun down.

Now we're 3-4 years into WooCommerce being an Automattic project and it's looking at nearly 60 million downloads, 4 million of which are active. A number they are saying is about 30% of all eCommerce on the web. Daaaaang. I've used WooCommerce a number of times and it always does a great job for me.

Direct Link to ArticlePermalink

The post WooCommerce appeared first on CSS-Tricks.

All the Small Things: How Not To Annoy Your Mobile Users

Usability Geek - Wed, 12/19/2018 - 1:00pm
Let us talk about Peter. He has just bought a great shirt. It is on trend. The feel, and the price point was a bargain. He gets home, and it feels great. But then. It starts with a tickle, then a...
Categories: Web Standards

Fighting FOIT and FOUT Together

Css Tricks - Wed, 12/19/2018 - 4:30am

Lots from Divya with the setup:

There are 2 kinds of problems that can arise when using webfonts; Flash of invisible text (FOIT) and Flash of Unstyled Text (FOUT) ... If we were to compare them, FOUT is of course the lesser of the two evils

If you wanna fight FOIT, the easiest tool is the font-display CSS property. I like the optional value because I generally dislike the look of fonts swapping.

If you want to fight them both, one option is to preload the fonts:

<link rel="preload" href="/fonts/awesome-l.woff2" as="font" />

But...

Preload is your friend, but not like your best friend ... preloading in excess can significantly worsen performance, since preloads block initial render.

Just like CSS does.

In a conversation with Scott Jehl, he pointed out to me that "preloads block initial render" isn't quite true, and he put together a test case. A <link rel="preload" ...> resource doesn't block rendering in and of itself.

Even huge sites aren't doing much about font loading perf. Roel Nieskens:

I expected major news sites to be really conscious about the fonts they use, and making sure everything is heavily optimised. Turns out a simple Sunday afternoon of hacking could save a lot of data: we could easily save roughly 200KB

Fonts are such big part of web perf, so let's get better at it! Here's Zach Leatherman at the Performance.now() conference:

Part of the story is that we might just have lean on JavaScript to do the things we need to do. Divya again:

Web fonts are primarily CSS oriented. It can therefore feel like bad practice to reach for JavaScript to solve a CSS issue, especially since JavaScript is a culprit for an ever increasing page weight.

But sometimes you just have to do it in order to get what you need done. Perhaps you'd like to handle page visibility yourself? Here's Divya's demonstration snippet:

const font = new fontFace("My Awesome Font", "url(/fonts/my-awesome-font.woff2)" format('woff2')") font.load().then (() => { document.fonts.add(font); document.body.style.fontFamily = "My Awesome Font, serif"; // we can also control visibility of the page based on a font loading // var content = document.getElementById("content"); content.style.visibility = "visible"; })

But there are a bunch of other reasons. Zach has documented them:

  • Because, as you load in fonts, each of them can cause repaints and you want to group them.
  • Because the user has indicated they want to use less data or reduce motion.
  • Because you'd like to be kind to slow networks.
  • Because you're using a third-party that can't handle font-display.

The post Fighting FOIT and FOUT Together appeared first on CSS-Tricks.

Google Fonts and font-display

Css Tricks - Wed, 12/19/2018 - 4:26am

The font-display descriptor in @font-face blocks is really great. It goes a long way, all by itself, for improving the perceived performance of web font loading. Loading web fonts is tricky stuff and having a tool like this that works as well as it does is a big deal for the web.

It's such a big deal that Google's own Pagespeed Insights / Lighthouse will ding you for not using it. A cruel irony, as their own Google Fonts (easily the most-used repository of custom fonts on the web) don't offer any way to use font-display.

Summarized by Daniel Dudas here:

Google Developers suggests using Lighthouse -> Lighthouse warns about not using font-display on loading fonts -> Web page uses Google Fonts the way it's suggested on Google Fonts -> Google Fonts doesn't supports font-display -> Facepalm.

Essentially, we developers would love a way to get font-display in that @font-face block that Google serves up, like this:

@font-face { font-family: "Open Sans Regular"; src: url("..."); font-display: swap; }

Or, some kind of alternative that is just as easy and just as effective.

Seems like query params is a possibility

When you use a Google Font, they give you a URL that coughs up a stylesheet and makes the font work. Like this:

https://fonts.googleapis.com/css?family=Roboto

They also support URL params for a variety of things, like weights:

https://fonts.googleapis.com/css?family=Open+Sans:400,700

And subsets:

http://fonts.googleapis.com/css?family=Creepster&text=TRICKS https://fonts.googleapis.com/css?family=Open+Sans:400,700&amp;subset=cyrillic

So, why not...

http://fonts.googleapis.com/css?family=Creepster&font-display=swap

The lead on the project says that caching is an issue with that (although it's been refuted by some since they already support arbitrary text params).

Adding query params reduces x-site cache hits. If we end up with something for font-display plus a bunch of params for variable fonts that could present us with problems.

They say that again later in the thread, so it sounds unlikely that we're going to get query params any time soon, but I'd love to be wrong.

Option: Download & Self-Host Them

All Google Fonts are open source, so we can snag a copy of them to use for whatever we want.

Once the font files are self-hosted and served, we're essentially writing @font-face blocks to link them up ourselves and we're free to include whatever font-display we want.

Option: Fetch & Alter

Robin Richtsfeld posted an idea to run an Ajax call from JavaScript for the font, then alter the response to include font-display and inject it.

const loadFont = (url) => { // the 'fetch' equivalent has caching issues var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onreadystatechange = () => { if (xhr.readyState == 4 && xhr.status == 200) { let css = xhr.responseText; css = css.replace(/}/g, 'font-display: swap; }'); const head = document.getElementsByTagName('head')[0]; const style = document.createElement('style'); style.appendChild(document.createTextNode(css)); head.appendChild(style); } }; xhr.send(); } loadFont('https://fonts.googleapis.com/css?family=Rammetto+One');

Clever! Although, I'm not entirely sure how this fits into the world of font loading. Since we're now handling loading this font in JavaScript, the loading performance is tied to when and how we're loading the script that runs this. If we're going to do that, maybe we ought to look into using the official webfontloader?

Option: Service Workers

Similar to above, we can fetch the font and alter it, but do it at the Service Worker level so we can cache it (perhaps more efficiently). Adam Lane wrote this:

self.addEventListener("fetch", event => { event.respondWith(handleRequest(event)) }); async function handleRequest(event) { const response = await fetch(event.request); if (event.request.url.indexOf("https://fonts.googleapis.com/css") === 0 && response.status < 400) { // Assuming you have a specific cache name setup const cache = await caches.open("google-fonts-stylesheets"); const cacheResponse = await cache.match(event.request); if (cacheResponse) { return cacheResponse; } const css = await response.text(); const patched = css.replace(/}/g, "font-display: swap; }"); const newResponse = new Response(patched, {headers: response.headers}); cache.put(event.request, newResponse.clone()); return newResponse; } return response; }

Even Google agrees that using Service Workers to help Google Fonts is a good idea. Workbox, their library for abstracting service worker management, uses Google Fonts as the first demo on the homepage:

// Cache the Google Fonts stylesheets with a stale while revalidate strategy. workbox.routing.registerRoute( /^https:\/\/fonts\.googleapis\.com/, workbox.strategies.staleWhileRevalidate({ cacheName: 'google-fonts-stylesheets', }), ); // Cache the Google Fonts webfont files with a cache first strategy for 1 year. workbox.routing.registerRoute( /^https:\/\/fonts\.gstatic\.com/, workbox.strategies.cacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new workbox.cacheableResponse.Plugin({ statuses: [0, 200], }), new workbox.expiration.Plugin({ maxAgeSeconds: 60 * 60 * 24 * 365, }), ], }), ); Option: Cloudflare Workers

Pier-Luc Gendreau looked into using Cloudflare workers to handle this, but then followed up with Supercharge Google Fonts with Cloudflare and Service Workers, apparently for even better perf.

It has a repo.

Option: Wait for @font-feature-values

One of the reasons Google might be dragging its heels on this (they've said the same), is that there is a new CSS @rule called @font-feature-values that is designed just for this situation. Here's the spec:

This mechanism can be used to set a default display policy for an entire font-family, and enables developers to set a display policy for @font-face rules that are not directly under their control. For example, when a font is served by a third-party font foundry, the developer does not control the @font-face rules but is still able to set a default font-display policy for the provided font-family. The ability to set a default policy for an entire font-family is also useful to avoid the ransom note effect (i.e. mismatched font faces) because the display policy is then applied to the entire font family.

There doesn't seem to be much movement at all on this (just a little), but it doesn't seem pretty awesome to wait on it.

The post Google Fonts and font-display appeared first on CSS-Tricks.

Ease-y Breezy: A Primer on Easing Functions

Css Tricks - Tue, 12/18/2018 - 9:28am

During the past few months, I’ve been actively teaching myself how to draw and animate SVG shapes. I’ve been using CSS transitions, as well as tools like D3.js, react-motion and GSAP, to create my animations.

One thing about animations in general and the documentation these and other animation tools recommend is using easing functions. I’ve been working with them in some capacity over the years, but to be honest, I would never know which function to choose for which kind of animation. Moreover, I did not know about the magic that goes into each of these functions, the notable differences between them, and how to use them effectively. But I was fine with that because I knew that easing functions somehow “smoothed” things out and mostly made my work look realistic.

Here, I present to you what I learned about easing functions in the form of a primer that I hope gives you a good understanding as you dig into animations.

How I got into easing functions

I tried to re-create a pattern called rotating snakes, an optical illusion that tricks the brains into thinking that circles rotate and “dance” when they are not.

I quickly found a gap in my knowledge when trying to build this out. It’s hard! But in the process, I discovered that easing functions play a big role in it.

I turned to JavaScript to draw a bunch of concentric circles in SVG using a library:

for (i = 1; i <= 10; i++) { drawCircle({radius: i * 10}); }

This was the result:

But that clearly does not look anything like the picture.

As I thought things through, I realized that I was looking for a certain property. I wanted the change in radius of the concentric circles to be small at the beginning and then become larger as the radius increases.

This means that the linear increase in radius using i++ won’t do the trick. We need a better formula to derive the radius. So, my next attempt looked something like this:

let i = 1; let radiusList = []; let radius = 0; while (i <= 10) { drawCircle({radius: i * 10}); if(i < 4) { i = i + 0.5 } else { i = i + 1 } }

...which got me this:

Hmm, still not what I wanted. In fact, this deviates even further from the pattern. Plus, this code is hardly customizable unwieldy to maintain.

So, I turned to math for one last attempt.

What we need is a function that changes the radius organically and exponentially. I had an “Aha!” moment and maybe you already see it, too. Easing functions will do this!

The radius of each circle should increase slowly at first, then quickly as the circles go outward. With easing, we can make move things along a curve that can slow and speed up at certain points.

A quick Google search landed me at this gist which is a well-documents list of easing functions and really saved my day. Each function takes one input value, runs formulae. and provides an output value. The input value has to be between 0 and 1. (We will dig into this reasoning later.)

A quadratic easing function looked promising because all it does is square the value it receives:

function (t) { return t*t }

Here’s the code I wound up using:

const easing = (t) => { return t*t } for(i = 0; i<=1; i=i+0.05) { const r = easing(i) * 40; drawCircle(r); }

And we have a winner!

The difference between this pattern and my first two attempts was night and day. Yay for easing functions!

This little experience got me really interested in what else easing functions could do. I scoured the internet for cool information. I found old articles, mostly related to Flash and ActionScript which had demos showing different line graphs.

That’s all pretty outdated, so here’s my little primer on easing functions.

What are easing functions?

They’re a type of function that takes a numeric input between 0 and 1. That number runs through the specified function and returns another number between 0 and 1. A value between 0-1 multiplied by another value between 0-1 always results in a value between 0-1. This special property helps us make any computation we want while remaining within specific bounds.

The purpose of an easing function is to get non-linear values from linear value inputs.

This is the crux of what we need to know about easing functions. The explanations and demos here on out are all geared towards driving home this concept.

Easing functions are a manifestation of the interpolation concept in mathematics. Interpolation is the process of finding the set of points that lie on a curve. Easing functions are essentially drawing a curve from point 0 to point 1 by interpolating (computing) different sets of points along the way.

Robert Penner was the first to define easing functions and create formulae for different ones in his book.

The five types of easing functions

There are five types of easing functions. They can be mixed, inverted and even mashed together to form additional, more complex functions. Let’s dig into each one.

Linear easing functions

This is the most basic form of easing. If the interval between the points we interpolate between 0 and 1 are constant, then we then form a linear easing function.

Going back to the concentric circles example earlier, increasing the radius of the initial circle by a constant amount (10px in that example) makes a linear function.

It should come as no surprise that linear is the default easing function. They’re extremely simple because there is no curve to the animation and the object moves in a straight, consistent direction. That said, linear functions have their drawbacks. For example, linear animations tend to feel unnatural or even robotic because real-life objects rarely move with such perfect, straight motion.

Quadratic easing functions

A quadratic easing function is created by multiplying a value between 0 and 1 by itself (e.g. 0.5*0.5). As we learned earlier, we see that this results in a value that is also between 0 and 1 (e.g. 0.5*0.5 = 0.25).

To demonstrate, let’s make 10 values between 0 and 1 with a quadratic function.

const quad_easing = (t) => t*t; let easing_vals = []; for(let i = 0; i < 1; i +=0.1) { easing_vals.push(quad_easing(i)); }

Here’s a table of all the values we get:

Input Value (x-axis) Quadratic Eased Value (y-axis) 0 0 0.1 0.01 0.2 0.04 0.3 0.09 0.4 0.16 0.5 0.25 0.6 0.36 0.7 0.49 0.8 0.64 0.9 0.81 1 1

If we were to plot this value on a graph with x-axis as the original value and y-axis as the eased value, we would get something like this:

Notice something? The curve is practically the same as the ease-in functions we commonly find, even in CSS!

Cubic, Quartic and Quintic easing functions

The final three types of easing functions behave the same, but work with a different value.

A cubic easing function is creating by multiplying a value between 0 and 1 by itself three times. In other words, it’s some value (e.g. t), cubed (e.g. t3).

Quartic functions do the same, but to the power of 4. So, if t is our value, we’re looking at t4

And, as you have already guessed, a quintic function runs to the power of 5.

The following demo will give you a way to play around with the five types of functions for a good visual of how they differ from one another.

See the Pen Plotting Easing functions by Pavithra Kodmad (@pkodmad) on CodePen.

Easing in and easing out…or both!

“An ease-in-out is a delicious half-and-half combination, like a vanilla-chocolate swirl ice cream cone.”
— Robert Penner

Ease in and ease out might be the most familiar easing animations. They often smooth out a typical linear line by slowing down at the start or end (or both!) of an animation.

Ease-in and ease-out animations can be created using any of the non-linear functions we’ve already looked at, though cubic functions are most commonly used. In fact, the CSS animation property comes with ease-in and ease-out values right out of the box, via the animation-timing-function sub-property.

  • ease-in: This function starts slow but ends faster.
  • ease-out: This function starts fast and ends slower.
  • ease-in-out: This function acts as a combination of the others, starting fast, slowing down in the middle, then ending faster.

See the Pen Easing demo by Pavithra Kodmad (@pkodmad) on CodePen.

Go ahead and play around with them on this cubic-bezier.com.

These curves can be created in JavaScript as well. I personally like and use the bezier-easing library for it. Easing.js is another good one, as is D3’s library (with a nice example from Mike Bostock). And, if jQuery is more your thing, check out this plugin or even this one.

See, it’s pretty “ease”-y!

I hope this little primer helps illustrate easing functions and interpolation in general. There are so many ways these functions can make animations more natural and life-like. Have a look at Easing.css for a UI that allows you to create custom curves and comes with a slew of preset options.

I hope the next time you use an easing function, it won’t be a blackbox to you. You now have a baseline understanding to put easing functions to use and open up a ton of possibilities when working with animations.

More on easing

We’ve only scratched the surface of easing functions here, but there are other good resources right here on CSS-Tricks worth checking out to level up even further.

The post Ease-y Breezy: A Primer on Easing Functions appeared first on CSS-Tricks.

How to Worry About npm Package Weight

Css Tricks - Tue, 12/18/2018 - 4:59am

It's all too easy to go crazy with the imports and end up with megabytes upon megabytes of JavaScript. It can be a problem as that weight burdens each and every visitor from our site, very possibly delaying or stopping them from doing what they came to do on the site. Bad for them, worse for you.

There is all sorts of ways to keep an eye on it.

You could have a peak on Bundlephobia

Bundlephobia will give you a look at the total size — both zipped and unzipped — along with download times, the number of required sub-dependencies it has, and whether or not it can be tree-shaked (tree-shook? tree-shaken?).

Speaking of "phobia," there is also Package Phobia that shows the publish and install sizes of individual packages:

You could let VS Code tell you right in the editor

Your import and require statements can lett you know the size of that particular inclusion with the Import Cost extension.

You could look at a data visualization

The Webpack Bundle Analyzer does that.

You could check out the sizes with text output

Cost of modules is another analyzer, but it seems like it only looks at package.json rather than the final bundle, then outputs it as a table:

Webpack Bundle Size Analyzer will show you a nested list of dependency weights, including the size of the bundle code itself, which is interesting:

package size will show you the weight of a comma-separated list of packages

package size has a simple CLI syntax for that:

You have another favorite way to keep your project in check?

The post How to Worry About npm Package Weight appeared first on CSS-Tricks.

Nobody is quite wrong.

Css Tricks - Mon, 12/17/2018 - 11:57am

There are two opposing views on using non-polyfillable new web features that I find are both equally common in our industry:

  1. Websites don't need to look the same in every browser. The concept of progressive enhancement helps with that. There are tools, even native language features, that help with this.
  2. If browser support isn't where I want it to be, it's just exotic eye candy for demos and not to be used.

I'm not sure I'd say either one of these is more or less correct than the other.

I also imagine it doesn't come as much of surprise that I support the thinking behind #1. It's perfectly possible to design and implement things that behave differently in different browsers and conditions. That's essentially what responsive design is, and that's pretty much the entire internet now.

The backbone of progressive enhancement is starting with a working foundation that works everywhere and layering design and functionality on top of that, when possible. There are even native language features to support the idea. @supports rules allow us to write CSS that can do something if a feature is supported and do something else if it isn't.

This is the entire use case for Modernizr and it has 22,804 stars.

I don't want to argue against progressive enhancement. Remember, I just said I support that thinking. But I do have some empathy for people and teams that choose not to go there, and end up developing more of a #2 attitude.

It is a bit more work to develop and design features that work in different ways. It might be work that is absolutely worth doing. Or it might not. Either way, it does complicate things. It's more code, it requires more attention and testing, and it's a bit harder to reason. It's technical debt.

Let me be preemptively defensive again: technical debt can be fine, and even intentional. We all incur it in everything we build. My point is that it is helpful to be smart about it and take on an amount of technical debt that is feasible for you to look after in perpetuity.

You might argue that building on a progressive enhancement foundation is, in a sense, less technical debt because you're building on such a sturdy foundation that less testing and constant tending to is required. Perhaps!

I do get behaving like a #2. It feels safer. It feels like you're being cautious and responsible. "Hey that's neat," you think. "I'll revisit it in a few years to see if I can use it for real." I might argue that 1) that's no fun and 2) almost counter-intuitively, it means you aren't willing to take a progressive enhancement approach which may make your code ultimately more frail.

It depends, I suppose. It depends on what exactly you're trying to do. It depends on the weight of that techinical debt. It depends on the team and the rate of developer churn. It depends on documentation. It depends on testing and QA.

You do you.

The post Nobody is quite wrong. appeared first on CSS-Tricks.

A CSS Venn Diagram

Css Tricks - Mon, 12/17/2018 - 11:42am

This is pretty wild: Adrian Roselli has made a series of rather complex Venn diagrams using nothing but CSS. With a combination of the Firefox dev inspector, plus a mixture of CSS Grid and the shape-outside property, it’s possible to do this and without a ton of hacks, too.

I also think it’s super cute that Adrian has made the code snippets in this post look like the display from an old monitor, like the one Chris recently broke down.

Direct Link to ArticlePermalink

The post A CSS Venn Diagram appeared first on CSS-Tricks.

Reversing an Easing Curve

Css Tricks - Mon, 12/17/2018 - 5:12am

Let’s take a look at a carousel I worked on where items slide in and out of view with CSS animations. To get each item to slide in and out of view nicely I used a cubic-bezier for the animation-timing-function property, instead of using a standard easing keyword.

See the Pen Carousel with reversed easing curve by Michelle Barker (@michellebarker) on CodePen.

A cubic-bezier can appear confusing at first glance, but when used correctly, it can add a nice touch to the user experience.

While building this carousel, I realized I not only needed a custom animation curve, but had to use it in reverse as well to get the effect right. I figured it was worth sharing what I learned because creating a custom curve and reversing it may seem tricky, but it’s actually pretty straightforward.

First, a primer on easing

Easing is the word used to describe the acceleration and deceleration of an animation’s progress along a timeline. We can plot this as a graph, where x is time and y is the animation’s progress. The graph for a linear animation, which has no acceleration or deceleration (it moves at the same speed all the way through), is a straight line:

Non-linear easing is what gives animations a more natural, life-like feel. We can apply easing to transitions and animations in CSS. The animation-timing-function property allows us to define the easing on an animation. (The same options are available for transition-timing-function.) We have four keywords to choose from:

  • linear – as described above
  • ease-in – the animation starts off slow and accelerates as it progresses
  • ease-out – the animation starts off fast and decelerates towards the end
  • ease-in-out – the animation starts off slowly, accelerates in the middle and slows down towards the end
  • ease – the default value, a variant on ease-in-out
  • Getting to know cubic-bezier

    If none of those options suit our animation, we can create a custom easing curve using the cubic-bezier function. Here’s an example:

    .my-element { animation-name: slide; animation-duration: 3s; animation-timing-function: cubic-bezier(0.45, 0.25, 0.60, 0.95); }

    If we prefer, we can write these properties as shorthand, like this:

    .my-element { animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95); }

    You’ll notice the cubic-bezier function takes four values. These are the two pairs of coordinates needed in order to plot our curve onto the graph. What do those coordinates represent? Well, if you’ve used vector illustration programs, like Illustrator, the idea of vector points and “handles” that control the size and direction of the curve might be familiar to you. This is essentially what we’re plotting with a cubic-bezier curve.

    We don’t need to know about all the maths behind cubic-bezier curves in order to create a nice animation. Luckily there are plenty of online tools, like cubic-bezier.com by Lea Verou, that allow us to visualize an easing curve and copy the values. This is what I did for the above easing curve, which looks like this:

    Here, we can see the two points we need to plot, where the cubic-bezier function is cubic-bezier(x1, y1, x2, y2).

    Applying easing in two directions

    My carousel rotates in both directions — if you click the left arrow, the current item slides out of view to the right, and the next item slides in from the left; and if you click the right arrow, the reverse happens. For the items to slide into or out of view, a class is added to each item, depending on whether the user has clicked the "next” or “previous” button. My initial assumption was that I could simply reverse the animation-direction for items sliding out in the opposite direction, like this:

    .my-element--reversed { animation: slide 3s cubic-bezier(0.45, 0.25, 0.60, 0.95) reverse; }

    There’s just one problem: reversing the animation also reversed the easing curve! So, now my animation looks great in one direction, but is completely off in the other direction. Oh no!

    In this demo, the first box shows the initial animation (an item sliding left-to-right) and the second box shows what happens when the animation-direction is reversed.

    See the Pen Reverse animation by Michelle Barker (@michellebarker) on CodePen.

    You can see the two animations don’t have the same feel at all. The first box speeds up early on and slows down gently as it progresses, while the second box starts off quite sluggish, then gains a burst of speed before abruptly slowing down. This wouldn’t feel right at all for a carousel.

    We have a choice between two options to achieve this:

    1. We could create a new keyframe animation to animate the items out, and then apply the same easing as before. That’s not too hard in this example, but what if we have a more complex animation? It would take quite a bit more work to create the same animation in reverse, and we could easily make a mistake.
    2. We could use the same keyframe animation (with the animation-direction: reverse) and invert the easing curve so we get the same easing in both directions. This isn’t too hard to do, either.

    To reverse the easing curve for our reversed animation we need to rotate the curve 180 degrees on its axis and find the new coordinates.

    We can do this with some simple maths — by switching the coordinate pairs and subtracting each value from 1.

    To visualize this, imagine that our original values are:

    x1, y1, x2, y2

    Our reversed values will be:

    (1 - x2), (1 - y2), (1 - x1), (1 - y1)

    In this demo, the third box shows what we want to happen: the item sliding in the opposite direction, but with the easing reversed to give it the same feel.

    See the Pen CSS Variables to reverse easing by Michelle Barker (@michellebarker) on CodePen.

    Let’s walk through how we can calculate the reversed easing curve.

    Calculating the new curve with CSS variables

    We can use CSS variables to calculate the new curve for us! Lets assign each value to a variable:

    :root { --x1: 0.45; --y1: 0.25; --x2: 0.6; --y2: 0.95; --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2)); }

    Then we can use those variables to calculate the new values:

    :root { --reversedCurve: cubic-bezier(calc(1 - var(--x2)), calc(1 - var(--y2)), calc(1 - var(--x1)), calc(1 - var(--y1))); }

    Now, if we make any changes to the first set of variables, the reversed curve will be calculated automatically. To make this a bit easier to scan when examining and debugging our code, I like to break these new values out into their own variables:

    :root { /* Original values */ --x1: 0.45; --y1: 0.25; --x2: 0.6; --y2: 0.95; --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2)); /* Reversed values */ --x1-r: calc(1 - var(--x2)); --y1-r: calc(1 - var(--y2)); --x2-r: calc(1 - var(--x1)); --y2-r: calc(1 - var(--y1)); --reversedCurve: cubic-bezier(var(--x1-r), var(--y1-r), var(--x2-r), var(--y2-r)); }

    Now all that remains is to apply the new curve to our reversed animation:

    .my-element--reversed { animation: slide 3s var(--reversedCurve) reverse; }

    To help visualize this, I’ve built a little tool to calculate the reversed values of a cubic-bezier. Enter the original coordinate values to get the reversed curve:

    See the Pen Reverse cubic-bezier by Michelle Barker (@michellebarker) on CodePen.

    The post Reversing an Easing Curve appeared first on CSS-Tricks.

Annotated Build Processes

Css Tricks - Fri, 12/14/2018 - 5:59am

When you're putting together a build process for a site, it's so dang useful to look at other people's processes. I ran across Andrew Welch's "An Annotated webpack 4 Config for Frontend Web Development" the other day and was glad he blogged it. If I was kicking off a new site where I wanted a webpack build, then I'd almost certainly reference something like this rather than start from scratch. At the same time, it made me realize how build processes all have such different needs and how unique those needs are now from even a few years ago in the hay day of Grunt and Gulp build processes.

I was looking around for an annotated Gulp reference file and came across another one of Andrew's articles — "A Gulp Workflow for Frontend Development Automation" — from just one year earlier! Here's a simplified list of what he was doing with Gulp (which he explains in more detail in the post):

  • Compile Sass
  • Run Autoprefixer
  • Create Sourcemaps
  • Minify
  • Inject critical CSS and bits of scripts
  • Run Babel
  • Uglify
  • Do style injection/reloading
  • Run accessibility audit
  • Generate icon font
  • Optimize images

Speaking of Gulp and annotated build processes, I'm working on a CSS-Tricks redesign and, for various reasons, went with a Gulp build. Against my better judgment, I wrote it from scratch, and this is how far I've gotten. It doesn't feel particularly robust or efficient, so rewrites and suggestions are welcome! And speaking of Gulp, here's a recently-published boilerplate I wish I had seen before starting.

Now, a year later, here's what the build process is being asked to do:

  • Run differently-configured web servers
  • Hot module replacement
  • Dynamic code splitting
  • Lazy loading
  • Make modern and legacy code bundles
  • Cache busting
  • Create service worker
  • Compile PostCSS
  • Optimize images / create .webp
  • Process .vue files
  • Run Tailwind and PurgeCSS

It's funny how quickly things change. We're still essentially asking for help compiling files and optimizing things, but the tools we use change, the code we write changes, the way we talk about development changes, the expectations of development changes, the best practices change... makes ya sweat. &#x1f605;

The post Annotated Build Processes appeared first on CSS-Tricks.

Making SVG icon libraries for React apps

Css Tricks - Fri, 12/14/2018 - 5:20am

Nicolas Gallagher:

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

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

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

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

Direct Link to ArticlePermalink

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

Two Ways to Build a Site That Seem Super Different But Weirdly Aren’t That Different

Css Tricks - Fri, 12/14/2018 - 5:16am

Here are two ways to build a site (abstractly) that feel diametrically opposed to me:

  1. Build a site as an SPA (Single Page App). The page loads a skeleton HTML page that executes JavaScript as quickly as it can. The JavaScript calls an API to get data, and then the page renders content. Navigation of the site is more API calls to get the data it needs and re-rendering.
  2. Build a site as statically-generated. A build process runs in which the entire site is built out as static HTML files with all the content baked into them. JavaScript isn't required at all for the site to work.

That feels just about as different as can be. But weirdly, they kinda aren't:

  1. They are both JAMstack. They can be hosted statically as neither of them needs backend languages running on the server they are hosted on.
  2. They are both building content based on an API of data. It's more obvious in the first one, but you can think of a static site generator as hitting an API of data as it runs and builds itself. It's just that the API might be temporarily created from content files it finds on disk. Or it might be the exact same API used for the former site.

That's all.

The post Two Ways to Build a Site That Seem Super Different But Weirdly Aren’t That Different appeared first on CSS-Tricks.

JavaScript to Native (and Back!)

Css Tricks - Thu, 12/13/2018 - 1:17pm

I admit I'm quite intrigued by frameworks that allow you write apps in web frameworks because they do magic to make them into native apps for you. There are loads of players here. You've got NativeScript, Cordova, PhoneGap, Tabris, React Native, and Flutter. For deskop apps, we've got Electron.

What's interesting now is to see what's important to these frameworks by honing in on their focus. Hummingbird is Flutter for the web. (There is a fun series on Flutter over on the Bendworks blog in addition to a post we published earlier this year.) The idea being you get super high performance ,thanks to the framework, and you've theoretically built one app that runs both on the web and natively. I don't know of any real success stories I can point to, but it does seem like an awesome possibility.

Nicolas Gallagher has been a strong proponent of React Native for the web.

The post JavaScript to Native (and Back!) appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.