Archive

Archive for December, 2018

Gulp for WordPress: Creating the Tasks

December 27th, 2018 No comments

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/assets/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/assets/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. ?

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

Categories: Designing, Others Tags:

7 Tips for Building a Highly Effective Agency Content Collaboration & Productivity

December 27th, 2018 No comments

Whether you’re new to your industry, or a hardened veteran, you’ll probably admit that your job isn’t always a walk in the park. There might be ways that you wish you can improve your workflow, even if it’s just a little. For that reason, we’ve put together a simple list of 7 tips for building a highly effective agency. Some of them might seem a little obvious, but there’s always room for improvement. Without further ado, let’s get this party started:

1. File Share And Access Permissions

Sharing information regarding a project is essential for progression. Fortunately, we don’t have to rely on emails and face-to-face interactions anymore. Now, we have helpful tools like Dropbox for file permissions and shared links which are available to Dropbox Professional and Business customers.

Dropbox allows everyone on your team to share and receive information faster than you can blink. The tool offers tons of online features to make sure your information is safe, and that it gets to where it needs to go quickly. Dropbox destroys the competition with their speed and service.

2. Track your time

If you aren’t already time tracking, you should stop what you’re doing now and look into it. There are a few reasons why time tracking is so important for everyone on your team:

  • For team members paid hourly, it ensures an accurate paycheck
  • It helps you better calculate time estimates for future clients
  • It helps you organize your day by keeping track of how long daily tasks take to complete

Keeping track of your time ensures that you’re not wasting time, therefore being as productive as possible.

3. Communicate

You know what they say, communication is the key to any relationship. The same can be said for team collaboration and productivity. You and your team should be communicating regularly and keeping each other in the loop. It can be so easy to lose track of a project if everyone isn’t caught up to speed.

From the start, your team should know exactly how to get into contact with each other. You should establish what channels everyone should use, and give them a specific purpose. You might have one line of communication open for quick comments, and another for emergencies only.

4. Know your team

Whether you’re remote or in a physical office, it’s important that you know exactly who you’re working with. You should take the time to get to know each and every one of your colleagues as best as you can. Learn their preferences, how they organize their desk, what their favorite sports teams are, and their birthdays. Being familiar with each other helps productivity levels simply because you’re not just working with a stranger anymore.

Because the methods for knowing your team vary wildly depending on whether you’re remote or on-site, we’ll split them up accordingly. Let’s start with remote team building ideas:

  • Video chat coffee break You don’t have to meet in person to enjoy a nice coffee and a friendly chat. Pull up a chair, and pretend that you’re both in the same coffee shop.
  • Virtual office tour A virtual office tour helps people understand how you organize your work day. It may not seem like much, and you may feel obliged to clean a little before the grand tour, but recording a quick little 5 minute walk around of your office is a very easy way for your colleagues to feel connected.

Now, let’s name off a few ideas that can help on-site teams get to know each other a little more:

  • Organize office birthday parties Throwing a simple birthday party for a colleague is an easy way to get everyone chatting. It’s a great excuse to order pizza, too.
  • Facts about yourself Listing some facts about yourself, interesting or not, is a great way for both remote teams and on-site teams to connect and learn a lot about each other.

The great thing about team building is that you can be as simple or creative as you want. There are tons of great team building exercises out there to try. The real idea is to get your colleagues familiar with the way they all function.

5. Make sure you hire the right people for the job

Let’s be honest, the market can be flooded with candidates for a job you’re trying to fill. The best tip anyone can give you as far as this topic goes is to take your time. It may seem like it’s taking forever to find the right fit, but I can assure you that the time spent finding the perfect employee will be more than made up for if they do their job correctly.

6. Encourage feedback

Nobody will learn anything if problems are ignored. You and your team should be open to giving and receiving feedback on everything. You have to remember that you have a specifically assembled team of experts in front of you, their opinions should matter just as much as yours.

7. Be ready for change

Ask anyone in any industry and they’ll tell you that plans change… a lot. You should welcome change by embracing it, not running from it. As much as you’d like to be perfect, the reality is that there will be some times where you aren’t, and that’s okay. The best thing to do is to prepare yourself in advance. Take some time to look for faults in your plan and adjust it accordingly.

Change should never be avoided. If a project is going in a certain direction, you should be capable enough to handle it. Just take a breather, and charge full steam ahead.

The end, or is it?

There are essentially a limitless amount of tips anyone can give you to help you build your agency effectively. What works for some might not work for others. Do your research and find the perfect fit for you and your team. It might involve a little trial and error, but you’ll get it in the end, and you’ll be so much better off for it.

This is a sponsored post for Dropbox. All opinions are my own. Dropbox is not affiliated with nor endorses any other products or services mentioned.

Read More at 7 Tips for Building a Highly Effective Agency Content Collaboration & Productivity

Categories: Designing, Others Tags:

Common CSS Issues For Front-End Projects

December 27th, 2018 No comments

Common CSS Issues For Front-End Projects

Common CSS Issues For Front-End Projects

Ahmad Shadeed

2018-12-27T13:30:11+01:002018-12-27T20:12:45+00:00

When implementing a user interface in a browser, it’s good to minimize those differences and issues wherever you can, so that the UI is predictable. Keeping track of all of those differences is hard, so I’ve put together a list of common issues, with their solutions, as a handy reference guide for when you’re working on a new project.

Let’s begin.

1. Reset The Backgrounds Of button And input Elements

When adding a button, reset its background, or else it will look different across browsers. In the example below, the same button is shown in Chrome and in Safari. The latter adds a default gray background.


(Large preview)

Resetting the background will solve this issue:

button {
  appearance: none;
  background: transparent;
  /* Other styles */
}

See the Pen Button and Inputs by Ahmad Shadeed (@shadeed) on CodePen.

2. Overflow: scroll vs. auto

To limit the height of an element and allow the user to scroll within it, add overflow: scroll-y. This will look good in Chrome on macOS. However, on Chrome Windows, the scroll bar is always there (even if the content is short). This is because scroll-y will show a scroll bar regardless of the content, whereas overflow: auto will show a scroll bar only when needed.


Left: Chrome on macOS. Right: Chrome on Windows. (Large preview)
.element {
    height: 300px;
    overflow-y: auto;
}

See the Pen overflow-y by Ahmad Shadeed (@shadeed) on CodePen.

3. Add flex-wrap

Make an element behave as a flex container simply by adding display: flex. However, when the screen size shrinks, the browser will display a horizontal scroll bar in case flex-wrap is not added.

<div class="wrapper">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>
.wrapper {
  display: flex;
}

.item {
  flex: 0 0 120px;
  height: 100px;
}

The example above will work great on big screens. On mobile, the browser will show a horizontal scroll bar.


Left: A horizontal scroll bar is shown, and the items aren’t wrapped. Right: The items are wrapped onto two rows. (Large preview)

The solution is quite easy. The wrapper should know that when space is not available, it should wrap the items.

.wrapper {
    display: flex;
    flex-wrap: wrap;
}

See the Pen flex-wrap by Ahmad Shadeed (@shadeed) on CodePen.

4. Don’t Use justify-content: space-between When The Number Of Flex Items Is Dynamic

When justify-content: space-between is applied to a flex container, it will distribute the elements and leave an equal amount of space between them. Our example has eight card items, and they look good. What if, for some reason, the number of items was seven? The second row of elements would look different than the first one.


The wrapper with eight items. (Large preview)

The wrapper with seven items. (Large preview)

See the Pen justify-content by Ahmad Shadeed (@shadeed) on CodePen.

In this case, using CSS grid would be more suitable.

5. Long Words And Links

When an article is being viewed on a mobile screen, a long word or inline link might cause a horizontal scroll bar to appear. Using CSS’ word-break will prevent that from happening.

Large preview
.article-content p {
    word-break: break-all;
}   

(Large preview)

Check out CSS-Tricks for the details.

6. Transparent Gradients

When adding gradient with a transparent start and end point, it will look black-ish in Safari. That’s because Safari doesn’t recognize the keyword transparent. By substituting it with rgba(0, 0, 0, 0), it will work as expected. Note the below screenshot:


Top: Chrome 70. Bottom: Safari 12. (Large preview)
.section-hero {
  background: linear-gradient(transparent, #d7e0ef), #527ee0;
  /*Other styles*/
}

This should instead be:

.section-hero {
  background: linear-gradient(rgba(0, 0, 0,0), #d7e0ef), #527ee0;
  /*Other styles*/
}

7. The Misconception About The Difference Between auto-fit And auto-fill In CSS Grid

In CSS grid, the repeat function can create a responsive column layout without requiring the use of media queries. To achieve that, use either auto-fill or auto-fit.

.wrapper {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

(Large preview)

In short, auto-fill will arrange the columns without expanding their widths, whereas auto-fit will collapse them to zero width but only if you have empty columns. Sara Soueidan has written an excellent article on the topic.

8. Fixing Elements To The Top Of The Screen When The Viewport Is Not Tall Enough

If you fix an element to the top of the screen, what happens if the viewport is not tall enough? Simple: It will take up screen space, and, as a result, the vertical area available for the user to browse the website will be small and uncomfortable, which will detract from the experience.

@media (min-height: 500px) {
    .site-header {
        position: sticky;
        top: 0;
        /*other styles*/
    }
}

In the snippet above, we’re telling the browser to fix the header to the top only if the viewport’s height is equal to or greater than 500 pixels.

Also important: When you use position: sticky, it won’t work unless you specify the top property.

Large preview

See the Pen Vertical media queries: Fixed Header by Ahmad Shadeed (@shadeed) on CodePen.

9. Setting max-width For Images

When adding an image, define max-width: 100%, so that the image resizes when the screen is small. Otherwise, the browser will show a horizontal scroll bar.

img {
    max-width: 100%;
}

10. Using CSS Grid To Define main And aside Elements

CSS grid can be used to define the main and aside sections of a layout, which is a perfect use for grid. As a result, the aside section’s height will be equal to that of the main element, even if it’s empty.

To fix this, align the aside element to the start of its parent, so that its height doesn’t expand.

.wrapper {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-gap: 20px;
}

// align-self will tell the aside element to align itself with the start of its parent.
aside {
  grid-column: 1 / 4;
  grid-row: 1;
  align-self: start;
}

main {
  grid-column: 4 / 13;
}

(Large preview)

See the Pen main and aside by Ahmad Shadeed (@shadeed) on CodePen.

11. Adding fill To An SVG

Sometimes, while working with SVGs, fill won’t work as expected if the fill attribute has been added inline in the SVG. To solve this, either to remove the fill attribute from the SVG itself or override fill: color.

Take this example:

.some-icon {
    fill: #137cbf;
}

This won’t work if the SVG has an inline fill. It should be this instead:

.some-icon path {
    fill: #137cbf;
}

12. Working With Pseudo-Elements

I love to use pseudo-elements whenever I can. They provide us with a way to create fake elements, mostly for decorative purposes, without adding them to the HTML.

When working with them, the author might forget to do one of the following:

  • add the content: "" property,
  • set the width and height without defining the display property for it.

In the example below, we have a title with a badge as a pseudo-element. The content: "" property should be added. Also, the element should have display: inline-block set in order for the width and height to work as expected.

Large preview

13. The Weird Space When Using display: inline-block

Setting two or more elements to display: inline-block or display: inline will create a tiny space between each one. The space is added because the browser is interpreting the elements as words, and so it’s adding a character space between each one.

In the example below, each item has a space of 8px on the right side, but the tiny space caused by using display: inline-block is making it 12px, which is not the desired result.

li:not(:last-child) {
  margin-right: 8px;
}

(Large preview)

A simple fix for this is to set font-size: 0 on the parent element.

ul {
    font-size: 0;
}

li {
    font-size: 16px; /*The font size should be reassigned here because it will inherit `font-size: 0` from its parent.*/
}

(Large preview)

See the Pen Inline Block Spacing by Ahmad Shadeed (@shadeed) on CodePen.

14. Add for="ID" When Assigning A Label Element To An Input

When working with form elements, make sure that all label elements have an ID assigned to them. This will make them more accessible, and when they’re clicked, the associated input will get focus.

<label for="emailAddress">Email address:</label>
<input type="email" id="emailAddress">
Large preview

15. Fonts Not Working With Interactive HTML Elements

When assigning fonts to the whole document, they won’t be applied to elements such as input, button, select and textarea. They don’t inherit by default because the browser applies the default system font to them.

To fix this, assign the font property manually:

input, button, select, textarea {
  font-family: your-awesome-font-name;
}

16. Horizontal Scroll Bar

Some elements will cause a horizontal scroll bar to appear, due to the width of those elements.

The easiest way to find the cause of this issue is to use CSS outline. Addy Osmani has shared a very handy script that can be added to the browser console to outline every element on the page.

[].forEach.call($$("*"), function(a) {
  a.style.outline =
    "1px solid #" + (~~(Math.random() * (1 

(Large preview)

17. Compressed Or Stretched Images

When you resize an image in CSS, it could be compressed or stretched if the aspect ratio is not consistent with the width and height of the image.

The solution is simple: Use CSS’ object-fit. Its functionality is similar to that of background-size: cover for background images.

img {
    object-fit: cover;
}

(Large preview)

Using object-fit won’t be the perfect solution in all cases. Some images need to appear without cropping or resizing, and some platforms force the user to upload or crop an image at a defined size. For example, Dribbble accepts thumbnails uploads at 800 by 600 pixels.

18. Add The Correct type For input.

Use the correct type for an input field. This will enhance the user experience in mobile browsers and make it more accessible to users.

Here is some HTML:

<form action="">
  <p>
    <label for="name">Full name</label>
    <input type="text" id="name">
  </p>
  <p>
    <label for="email">Email</label>
    <input type="email" id="email">
  </p>
  <p>
    <label for="phone">Phone</label>
    <input type="tel" id="phone">
  </p>
</form>

This is how each input will look once it’s focused:


(Large preview)

19. Phone Numbers In RTL Layouts

When adding a phone number like + 972-123555777 in a right-to-left layout, the plus symbol will be positioned at the end of the number. To fix that, reassign the direction of the phone number.

p {
    direction: ltr;
}

(Large preview)

Conclusion

All of the issues mentioned here are among the most common ones I’ve faced in my front-end development work. My goal is to keep a list to check regularly while working on a web project.

Do you have an issue that you always face in CSS? Let us know in the comments!

Smashing Editorial(dm, ra, al, yk, il)
Categories: Others Tags:

How to Stop SEO Disasters During Website Migration

December 27th, 2018 No comments

After many weeks and months of preparations, you are ready to go; you’ve done everything that you needed to do to make sure that the design and development of the new site has been put together perfectly and you are happy to move forward.

So you press the launch button (because that’s how it works, right?), you sit back and congratulate yourself and your colleagues on relaunching the new website successfully…

…only to see the organic traffic has plummeted.

You look in fear, but then stop for a second because everyone knows that a site relaunch can sometimes cause an initial slump in organic traffic. You assume that it’s normal.

But after days, weeks and months, the traffic doesn’t recover, and panic sets in…

This is a potential reality for anyone who is relaunching a website. Amidst all the excitement with the new design and new features, not everyone thinks about the consequences that could happen if you don’t plan it effectively, especially from an SEO perspective.

In order to minimise any potential disasters, there’s a simple step-by-step process that you should follow in order to make sure that the periods before, during, and after the launch go smoothly and to better your long-term SEO strategy.

What Qualifies as Website Migration?

Google is surprisingly not very specific about moving sites and what it involves. They have two pages: one for site moves with URL changes and one without URL changes.

But it can actually get a lot more detailed and complex than that.

Let’s look at some of meanings of website migration and what it can involve:

  • you are changing domains and are planning to move from one domain to another e.g. during a rebrand;
  • you are going international and require to change the TLD (Top Level Domain) e.g. from .co.uk to globally recognised .com;
  • you want geo-specific TLDs and sub-folders e.g. .com/uk, .com/fr, .com/ca;
  • you are going to undertake structural changes e.g. changing the internal linking, changing the site hierarchy, changing the user journey;
  • you are changing from HTTP to HTTPS;
  • you are going to change the CMS (content management system) or platform you are currently on;
  • you are redesigning a website completely;
  • you are changing the mobile setup by applying AMP (Accelerated Mobile Pages) or PWA (Progressive Web Apps);
  • you are going through content changes e.g. adding/removing pages, introducing new languages, consolidating pages.

What to be Aware of Before Starting the Migration

Every migration is different, but there are some things that you need to be aware of before you even touch the website:

  • Work out your strategy – Do you need to do this? Why? What are you hoping to achieve? What are your objectives?
  • Who is going to be involved in this project? Get them involved as early as possible – whether you like it or not, you can’t do it alone. Make sure you talk to the relevant stakeholders to understand how it can impact them and how they can help to minimise disruptions. And the earlier they can be involved, the better.
  • Get professional SEO consultations to help you – much of the tasks listed below will involve someone with SEO expertise before, during and after the site has relaunched.
  • Get professional UX and CRO consultations to help you – you also need to follow the important elements of UX e.g. what kind of impacts certain design changes are going to have on user engagement and how it will affect conversion rates.
  • Looks aren’t everything – in the middle of all the excitement about building something visually stunning, make sure it doesn’t come at a usability or SEO cost. Sure, add a bit of flair and style to it, but don’t go over the top.
  • Get everyone to test – all the stakeholders should help with testing. This also applies if you have the development site already set up and are available to test.
  • Put time aside for fixing bugs and errors after launch – no, you can’t relax after you’ve done your bit
  • Site migration is not a solution for penalty – if you are suffering from any algorithmic penalties, it will not disappear during the migration. This will need to be fixed manually.
  • Never migrate your site during peak seasons – so if your busy period is Christmas, then don’t migrate the site between October and January

Before You Rebuild Your Website

  1. Crawl all URLs using tools like Screaming Frog SEO Spider and Sitebulb
    • Compile a list of all the URLs and add traffic performance (visits, bounce rates, exit rates, conversion) to use as a benchmark post-launch;
    • Eliminate any duplicate/low quality contents by redirects or improving them;
    • Check for broken links;
    • Check for broken pages;
    • Make sure all the relevant pages are accessible to search engines;
    • Make sure all the pages are accessible to humans (blind users, mobile users, browser compatibility).
  2. Compile the list of the new URLs.
  3. Plan out your new URL structures and site hierarchy/architecture.
  4. Carry out keyword research for every single page.
  5. Compile a list of your top keywords and note their rankings.
  6. Update or create new content for the new pages and include relevant keywords.
  7. Map out the 301 redirects from old to new URLs (and avoid redirect chains) in a spreadsheet.
  8. Identify and compile a list of your most important backlinks.
  9. Measure the page speed using tools like GTmetrix and WebPageTest.org.
  10. Set-up the new social media profiles if you are rebranding the name.
  11. Register and configure the new domain in Google Search Console.
  12. Carry out usability testing to prevent bad experiences from happening on new site.
  13. Plan your relaunch campaign – “hey we launched a new website” should happen soon after the launch rather than later. Who can help you with that? Current customers/clients/suppliers/bloggers/PRs etc.

During the Redesign

  1. Block development site with meta noindex tag or robots.txt to prevent duplication issues on Google.
  2. Make sure web analytics are implemented and tested on all pages.
  3. Publish the new URLs and content on the development site.
  4. Add/update title tags, meta description and alt texts to new pages.
  5. Add Google Tag Manager.
  6. Add any necessary retargeting and remarketing codes e.g. Facebook Pixels and Google Remarketing.
  7. Set up and verify your new Google Search Console account.
  8. Remove or update internal links which are pointing to broken or removed pages.
  9. Update your XML sitemaps and have it ready to submit on Google Search Console.
  10. Update all canonical tags and self-canonicalize all new pages.
  11. Update all internal links.
  12. Update your robots.txt.
  13. Create a custom 404 page.
  14. Crawl the site and verify that all 301 redirects are working.
  15. Add schema to create rich snippets opportunities.
  16. Add Open Graph fields for further rich media experience.
  17. Ask the relevant stakeholders to help with testing.
  18. When nearing the relaunch, attempt to organize usability testing for small amount of traffic or by using focus groups to iron out any issues.
  19. Ensure the site is compatible on most popular browsers and mobile devices.
  20. Ensure the site is accessible to visually-impaired users.

After the Relaunch

  1. Submit a change of address via Google Search Console.
  2. Submit new XML sitemap.
  3. Update all social media bios with new URLs.
  4. Crawl the new site and check that the redirects are working, all internal and external links are working, and fix any 404 pages.
  5. Crawl the list of URLs that you have extracted originally and verify their redirects.
  6. Add annotations on Google Analytics to make sure you know when the site has relaunched and subsequent changes.
  7. Update backlinks with new URLs by contacting those who have linked to you.
  8. Continuously monitor the web traffic, engagement and conversion as well as page speed.
  9. Test the mobile friendliness of your site using the Mobile Usability feature of Google Search Console.
  10. Benchmark those performance metrics against the old site.
  11. Reach to the authoritative sites that link to you and ask them to update the link to the new site.
  12. Monitor the indexed page count via Google Search Console and using the site: search on Google.
  13. Monitor your search rankings over time.
  14. Keep control of the old domain just in case of any issues.
  15. Organize new usability testings of the new site.
  16. Launch your relaunch campaign.

Choosing to Migrate or Not

Relaunching can be a good thing as it allows you to deliver the best user experience possible for your audience, a chance to refresh your brand and improve the bottom-line for your clients.

But that is only if you do it for the right reason and you plan the site migration properly.

You know the old saying by Benjamin Franklin: “if you fail to plan, you are planning to fail”.

If you plan and execute your relaunch successfully, this will give you the best chance of catching any problems as early as possible and to make the process a smooth one.

Because you do not want to go through the headache of a botched migration.

Featured image via DepositPhotos.

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

Source

Categories: Designing, Others Tags:

Gulp for WordPress: Initial Setup

December 26th, 2018 No comments

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 that 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 (Coming Tomorrow!)

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 . 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 when 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 the files that are ready for distribution.
  • Bundle Task: This task will simply run the build task, will 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 prodcution)
    └── 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 that 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@next

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. The command above uses the @next which installs version 4.0. Removing that will install 3.9.1 (or whatever the current version is) instead.

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.

There are some cases where we won't have to call the cb() function, 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.

Categories: Designing, Others Tags:

Quiz: Could You Become a Design Consultant in 2019?

December 26th, 2018 No comments

According to a recent QuickBooks survey, the #1 reason freelancers go into business for themselves is because it lends them the freedom to shape their own career path.

Whether you’ve done freelance web design work for a few months or a few years, there may come a time when you feel bored, unchallenged, or limited by it. When that happens, do you keep charging along because it’s what you originally set out to do? Or do you work on turning your career path into something that better aligns with your goals and job preferences?

One possible career pivot I want to present to you today is web consulting. Be sure to scroll down and take the quiz to see if this is a smart move for you!

Designer vs. Consultant: What’s the Difference?

A web designer or developer is someone who actually gets their hands dirty. They’re the ones who use coding and design skills to build a website from the ground up. Projects usually only last a couple months, unless maintenance services are offered afterwards.

A web consultant is an advisor for those in need of or who already have a website. They can provide a one-time assessment to clients or work as a dedicated advisor and guide.

Consultants specialize in the total landscape—from user persona research to optimization of a website and related marketing activities post-launch. As such, a web consultancy enables you to offer as little or as much as you’d like, unlike web design services which are a bit more rigid in nature.

In fact, selling consulting as an add-on to your web design plans could prove quite lucrative in and of itself. Not only would you become a total end-to-end provider of website services, but this would help you retain clients over longer periods of time.

Plus, as website builder tools grow more and more popular, you may find that many of the clients you would’ve easily sold design services to a year ago now confidently believe they can build a website on their own. And they have a point. Builders have greatly simplified the work that goes into creating professional-looking websites.

What these builders haven’t been able to do, though, is teach everyone how to choose the right color palette for accessibility or the right typeface for mobile users. Nor do page builders explain the importance of things like security and speed in the grand scheme of SEO. They may remove the need for someone to do hands-on work on a website (at least in your clients’ eyes), but they haven’t taught these DIY users the why of it all.

Take the Quiz: Are You a Designer or a Consultant?

I don’t mean to make this a completely black-or-white question. I believe that you can still build websites for a living while also providing occasional consulting to clients. Or vice versa. In fact, performing a mix of duties might be the perfect way to spice up your workday while bringing some much-needed stability to your income.

Use the following quiz to shed some light on whether or not website consulting is a viable path for you:

If you’re an implementer through-and-through, consulting isn’t a good choice for you.

If you’re not happy with the job anymore, it’s time to look at another career path, like consulting.

Consultants are inherently great at project management. If you don’t have the skills or interest, don’t go down that path.

Small business owners would appreciate the guidance, but won’t be able to afford your services. Enterprise-level companies will want the total package from you, so unless you have an agency, it may be best to hold off on approaching them.

If you’re not a people-person, consulting will be a very bad fit.

Consultants aren’t just people-persons. They’re also know-it-alls (but in the good sense).

Consultants are voracious learners. They have to be if they want to provide guidance that’s well-informed and valuable.

Unless you plan on providing one-off consulting services to clients, planning to consult, design, and develop by yourself just isn’t sustainable.

Is Web Consulting for You?

Not everyone is cut out for web consulting. And that’s fine. There are other ways to provide high-priced and recurring services to clients. Like selling website support or maintenance services.

But if you’re unhappy with what you’re doing now, don’t let your discontent affect the quality of your work. Find a way to fix it by pursuing a career path that makes the most sense for you.

Featured image via DepositPhotos.

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

Source

Categories: Designing, Others Tags:

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

December 24th, 2018 No comments

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 .

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 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.

A screenshot of clip-path: path() working in Firefox Nightly

Now, all we need is:

  • clip-path to be able to point to the URL of a in SVG, like url("#clip-path");
  • shape-outside to be able to use path()
  • shape-outside to be able to use a
  • 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

?

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

Categories: Designing, Others Tags:

What We Wished For

December 24th, 2018 No comments
Form Design Patterns — a practical guide for anyone who needs to design and code web forms

What We Wished For

What We Wished For

Mat Marquis

2018-12-24T14:30:46+01:002018-12-27T20:12:45+00:00

I think we’re headed for trouble, though I can’t say for sure. Trouble — trouble I know. The on-ramp to it, though; I’ve only heard about that. I’ve only been doing this for ten years. I missed all the lead-up the last time around. What I can say for sure — what I know from experience — is that I’ve never had a wish made in anger come true without regretting it.

Ten years (I don’t mind saying) is a pretty long time. Back when I first truth-stretched my way into a web design internship, good ol’ Internet Explorer was already a laughingstock.

“If you notice that a piece of your content appears and disappears, and sections of the page only get half-drawn, these are good indications that an element requires a layout. […] A hasLayout fix involves nothing more than declaring a CSS property that causes an element to gain a layout, when it wouldn’t ordinarily have a layout by default.”

The Internet Explorer hasLayout Property

I hated IE. I feel like I can cop to that now. I tried not to; I really, sincerely did. I’d tell people it was fun to support, if you can believe it.

As all the other browsers got easier and easier to deal with, I attempted to convince myself that there was at least still a challenge to quirky old IE. That even became something of a point of pride: I had gotten so good at fixing obscure IE issues that I’d learned to dodge them during the course of my everyday development, leaving nothing (well, less) to dread come the big “open it up in IE and see what broke” phase.

It’s fun, in a way. Fun. That was the lie I told myself.

/* Fixes #2588: When Windows Phone 7.5 (Mango) tries
to calculate a numeric opacity for a select (including
“inherit”) without explicitly specifying an opacity on
the parent to give it context, a bug appears where
clicking elsewhere on the page after opening the select
will open the select again. */

jQuery Mobile source

I hated it. I full-on, bad-jokes-in-a-conference-talk hated IE, in every one of its incarnations. I hated it every bit as much everybody else did.

“Internet Explorer 6 has a puzzling bug involving multiple floated elements; text characters from the last of the floated elements are sometimes duplicated below the last float. … The direct cause is nothing more than ordinary HTML comments, such as, , sandwiched between floats that come in sequence.”

Explorer 6 Duplicate Characters Bug

A waste of my goddamned time is what it was. All those hours I spent hunched over a janky virtual machine—reload, wait, throw a nonsense fix at a nonsense bug, reload, crash, open IE again, wait, double-check that caching wasn’t a factor, reload, wait, and repeat. I could have been doing so much more with my time — I could have learned so much more.

I was certain that it didn’t just hold back my work, and it didn’t just hold the web back, but it held me back, as a developer. On that second point, I guess I wasn’t entirely wrong — all the obscure IE 6-7 browser bug knowledge I accumulated is all useless now. All I have to show for it are an involuntary flinch at the word “filter,” an inscrutable preference for padding over margin, and a deep-seated but largely unfounded fear of z-index.
?

“…extra whitespace causes the wrong styles to be picked up if the actual class name is a substring (or superstring) of some other class name.”

IE5/Mac whitespace parsing bug

I wished it would go away. Uninstalled by a clever and widespread virus, banned by law, Microsoft finally deciding to cut their shoddy rendering engine’s losses and switching to Firefox’s rendering engine, Gecko — whatever — just make it go away. But… no. The web kept evolving and we developers beat on, boats against the current, borne back ceaselessly into the past.

Chrome came along, Firefox kept getting better, new features kept rolling out, the exciting and endless possibilities presented by the advent of responsive web design spread out before us, and also (quick aside) remember that you’ll only have a couple of days to make it all more-or-less work in old IE, so don’t get too carried away.
?

“IF you are using IE8, AND you are using the CSS ordered list numbering approach described above, AND the HTML that has the classes that use the counter-reset and counter-increment CSS attributes is HIDDEN when the page loads, THEN whenever that hidden HTML is displayed, ALL of the automatic numbers will be ZERO, BUT ONLY IF THE CSS :hover PSEUDO-CLASS IS USED ON THAT PAGE!”

The IE8 “hover” Bug: The Most Awesome IE Bug Ever?

It’s hard to imagine experiencing that kind of frustration nowadays, at least for us relatively-old-timers. Not to say that there isn’t an incredible amount of work involved in tuning things up cross-browser these days, too — I know all too well that there is. But it’s tough not to feel the occasional pang of, “back in my day, all we had were floats, and let me tell you about IE’s double margin bug,” when you hear about a little difference in how CSS Grid works from one browser to another.

I was wrong; I want to be clear on that point. Not wrong for being frustrated. I don’t think anyone should be blamed for being frustrated with those old browser bugs, same as I don’t think anyone should be blamed for their frustration with any aspect of web development now. No, I was wrong for the conclusion that anger brought me to: the desire to see Trident burned to the ground and the earth where it once stood salted.

I suspect that only one dramatically-ironic thing grows out of that salted earth: those same frustrations, born anew, for a new generation of web developers. When I started my career, scant few years after the browser wars, those seeds had already taken root. Because, for a time — a time before my own — we web developers cursed Netscape the same way. The weaker, buggier, inarguably worse browser. But Internet Explorer — developers loved that browser. And they wished those other browsers — the bad browsers — would just go away: uninstalled by a clever and widespread virus, banned by law, Netscape finally deciding to cut their shoddy rendering engine’s losses and switch to IE’s rendering engine, Trident — whatever — just make it go away. Those inscrutable Internet Explorer bugs didn’t happen by coincidence or negligence. They came about because Internet Explorer had won, and we loved it for winning.

See, our frustration and our anger lied to us, as they usually do. They told us that supporting those other, worse browsers didn’t just hold back our work, and didn’t just hold the web back, but it held us back, as developers. A waste of our goddamned time is what it was. So, we told ourselves that it wasn’t only for our own good, but for the good of the entire web.

We weighed IE just a little more heavily. We gave it just a little more say in our decisions. And so, holding so many chips, Microsoft played their cards accordingly — who could blame them? Everyone built websites for them first, and the others second. Their word wasn’t law, but it was certainly more than suggestion. Sure, they deviated from web standards here and there (just a little bit), but after all, wasn’t something implemented by The Biggest Browser a sort of de facto standard anyway? Besides, supporting the better, faster, easier browser was doing the web itself a service! Together with Microsoft, we were pushing the web forward! Everybody wins.

The rendering engine that powers Microsoft’s Edge browser today — EdgeHTML — is a fork of gnarly old Trident. It’s a stripped-down and vastly improved fork of Trident, for sure, but it isn’t, let’s say, universally judged on its own merit. The EdgeHTML team has always been working with a couple of disadvantages: the first was technical, in that it took a tremendous amount of time and effort to catch up with the likes of Safari, Firefox, and Chrome. The second was emotional. It was us — you and me — jaded from years of Internet Explorer, staring down a cheery blue lowercase “e” with cold disdain.

A few weeks ago, the Edge team announced that they’d soon be abandoning EdgeHTML in favor of Blink, the rendering engine that powers Chrome. With this change, the last few remaining embers of Trident will be snuffed out forever. The wish I’d shared with so many will finally be granted. Ironically timed — as it turns out — EdgeHTML was becoming a pretty solid rendering engine.

Blink is an open-source project led and governed by Google. It powers both Chrome and Opera, the latter of which similarly abandoned their home-grown rendering engine a few years ago.

By an overwhelming margin, Blink is (and will increasingly be) the way the web is experienced the world over. Blink is fast, stable, packed with modern features, and — by comparison to development for the still-evolving EdgeHTML — painless.

It may have happened too late to save us from those ancient IE bugs, but our work will be easier now that there’s one less rendering engine to support. You and I will lose a little more of our collective “but does it work cross-browser” burden. Our projects will go more smoothly, and the web will lose just a little more of what was once holding it back.

As stewards of the engine powering so very much of the web, well, Google’s word won’t be law, but certainly more than suggestion. And maybe over the course of the next few years, they’ll deviate from web standards here and there (whether intentionally or accidentally) in the tiniest of ways. But after all, isn’t something implemented by The Biggest Browser a sort of de facto standard itself? Besides, how could you argue? Favoring the better, faster, more powerful browser is doing the web itself a service, after all. Together with Google, we’ll be pushing the web forward. Everybody will win.

That is, as long as little standards deviations and tiny, nagging bugs don’t grow bigger over time — thanks to the twin forces of entropy and complacency. Unless the decisions we’ve made for the good of the web (hand-in-hand with a notoriously privacy-hostile advertising company) begin to feel a little darker, and a new bogeyman begins to take shape in our minds — unless we find that our old fears and frustrations have risen again (like a phoenix that renders a couple hundred pixels away from where it should and flickers in a weird way when you scroll).

It doesn’t take much imagination to see newer, more exciting rendering engines appearing over the next handful of years. It takes just as little imagination to see them failing due to lack of support, as we favor “the browser that everyone uses” — first by choice, and later perhaps in grudging service of “the bottom line.”

Again, though, I don’t know. I’ve never seen this happen with a rendering engine myself. I’ve just heard the whole story, and I only know first-hand how it ends. I know the ending from the ache of old psychic scars; from an involuntary flinch at some bits of code, and muscle-memory that compels me to avoid others. I know it from the jokes in conference talks that always felt a little tired, but still resonated just the same in a way I wouldn’t allow myself to admit and still spoke to a secret wish that I held deep in my heart. A bitter, hateful wish.

But hey, listen. Not anymore. Now, I mean — I would never. I really do love a good rendering engine bug, now. I do.

“CSS 3D transforms with perspective() are rendered inside out.”

bugs.chromium.org

I mean, that’s actually kind of a fun bug, right? Like, fun in a way. Y’know?

It’s fun.

It’ll be fun.

Smashing Editorial(dm, ra, il)
Categories: Others Tags:

The State of Web Design, December 2018

December 24th, 2018 No comments

Hey there, WDD Readers. Once more, I have spent a whole lot of my life online. My New Year’s resolution is to stay inside and keep doing that. I feel I shall probably succeed.

I have lived and breathed Internet stuff all year, and once again, I don’t even know how much I don’t know. The Internet holds untold numbers of secrets, most of them fairly dull, some of them delightful, and some of them are disgusting enough to inspire a certain awe and respect.

It’s a world unto itself. In my little corner of this world, I’ve once again noticed a few trends, and that’s what I’m here to write about today.

Aesthetic Fusion

Have you ever had Japanese food in Mexico? I’ve consulted with the experts (that is to say, actual Japanese people), and they’ll tell you that the Japanese food here is nothing at all like the real thing. Mexicans took Japanese food, and gave it their own spin, because apparently deep-fried sushi with some avocado in the middle sounded like a good idea to someone down here.

Heck, it sounds good to me. I love avocado as only a millennial raised in Mexico can.

Some people call this “fusion”—others have less complimentary names for it—and it’s happening in web design, too. Over the last couple of years, distinct design aesthetics burst onto the scene, and people went all out for brutalism, postmodernism, and more. Trends were popping up out of seemingly nowhere and taking on a life of their own, at speed.

This last year, though, it seems the emergence of distinct trends has slowed down. I’ve seen a lot more aesthetic fusion going on, with post-modern elements being mixed into fancy, elegant and serif-heavy designs, classical minimalism being given a hint of brutalism, and so on. It’s an interesting twist, and these fusions may eventually become aesthetic schools in their own right, one day. I’ll be watching with interest.

The Return of the “Business” Aesthetic

I’ve also noticed an upswing in the number of sites (including non-business sites) that look a bit… well… old. Not vintage or retro, but purposefully dated back to that era when almost every template was a “business” template.

Don’t get me wrong, these sites are built with things like Flexbox and CSS Grid, and they are clearly polished to a level consistent with everything we know about design now. But the bones of the design are old, maybe a little boring, and very business-friendly.

My theory is that after so much artsy design, some customers are asking their designers to go back to something a little more comfortable and familiar. It probably doesn’t help that many of the more experimental design trends can suffer in the usability and accessibility departments.

My prediction: This trend will continue, but only to a point. Some clients will be more concerned with standing out while other will just ask for something that works, and looks like something they expect of a professional business.

We’re Hardly Hearing About VR Anymore

VR is not dead, but we are hearing less and less about it, especially in regards to web design. Though it has been heralded as the next frontier for just about every kind of media, we’re seeing a general slowing of the industry. It is, for now, a niche.

the prophets of the past couple of years got their burning bushes a little too early

It’s not that the platform doesn’t have potential, it’s that the hardware is still prohibitive. It’s prohibitively expensive. It prohibits easily switching from VR to non-VR tasks. It’s nigh-impossible to use outside for a variety of reasons, where most people who aren’t us nerds spend a lot of time.

VR is coming, but the prophets of the past couple of years got their burning bushes a little too early, I think.

Front-end Code is More Developerized

Last year, I went over the battle being waged between groups I called the Experimentalists and the Standardistas. The Experimentalists are currently on a rising tide, buoyed by an influx of back end developers who are attempting to rewrite front end code in their image.

Seriously, the idea of CSS-in-JS used to be a joke, a subject of April Fool’s jokes and satirical articles. People aren’t joking about it so much anymore. The Standardista camp is blaming efforts to “developerize” front-end code on devs who never learned the original concepts and intent behind the way HTML is written, or things like the cascade.

the idea of CSS-in-JS used to be a joke, a subject of April Fool’s jokes and satirical articles

I think that’s more of a symptom than a cause, though. I blame the now nearly-constant demand for full-stack developers. While it’s certainly helpful to know a bit about how “the other side” works, there seems to be a rising aversion to specialization in the job market. As a generalist myself, I can understand that. But specialists exist for a simple reason: the Internet is too darned big for any one person to ever really be able to do it all on their own.

(It’s fairly obvious that I lean towards Standardista myself, isn’t it?)

Content Gets Blocky

I’d be remiss, nay, downright negligent if I didn’t address this: While some CMS creators are focusing on simple, text/Markdown-only solutions (which absolutely have their place), web content in major CMS options seems to be going increasingly the way of LegoTM. It started with more limited CMS like Medium, and is being expanded upon in newer iterations, like WordPress’ Gutenberg.

Block-based content is that most elusive of solutions: a compromise. It combines some flexibility and choice on the part of the user with a degree of control for the designers and developers. It can’t stop bad decisions (few things can stop the really bad ones), but I believe with all my little designer heart that it’s the way forward.

I guess we’ll see where it takes us. Same time next year?

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

Source

Categories: Designing, Others Tags:

Popular Design News of the Week: December 17, 2018 – December 23, 2018

December 23rd, 2018 No comments

Every week users submit a lot of interesting stuff on our sister site Webdesigner News, highlighting great content from around the web that can be of interest to web designers.

The best way to keep track of all the great stories and news being posted is simply to check out the Webdesigner News site, however, in case you missed some here’s a quick and useful compilation of the most popular designer news that we curated from the past week.

Note that this is only a very small selection of the links that were posted, so don’t miss out and subscribe to our newsletter and follow the site daily for all the news.

10 Exciting Web Design Trends You Can’t Hide from in 2019

Inspirational Websites from 2018

You’ve Seen this Font Before, but You Probably Don’t Know its Name

Google Design’s Best of 2018

A CSS Venn Diagram

How to Improve UX of Web Forms

Millitext, a 1px Wide Font (LCD)

The 9 Big Design Trends of 2019

19 Typography Tips that will Change the Way You Design for the Mobile Web

Embracing Open Design in 2019

We Should Replace Facebook with Personal Websites

The Cost of Living in Mark Zuckerberg’s Internet Empire

Redesign, but Make it Satire

10 Diagrams to Help You Think Straight About UX Research

Design Debate: Should You Work In-House or Freelance?

Our Favorite UX Initiatives this Year

How to Delete Facebook Without Losing your Friends and Photos

My Struggle with Colors

Spotify.design

Design Handoff: 7 Things Must Known About Design Specs for Developers

The Real Reason Clients won’t Pay You to Design their Website

The End of the Ad-Supported Web

Building a “Choose your own Adventure”-Style Game Engine in 48 Hours

Everything I Learned in 10 Years of Blogging

Why so Many Brands on Instagram Look the Same

Want more? No problem! Keep track of top design news from around the web with Webdesigner News.

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

Source

Categories: Designing, Others Tags: