Advanced CSS | Production and PostCSS


 

Production and PostCSS

Automated task runners like Grunt will save you time and make you money.

We have studied two exciting new Web design tools in this class: flexbox layout styles and SVG images.

When you work as a Web designer, it is important to stay on the cutting edge and try out new technologies. This is what keeps the Web moving forward. However, you also want your sites to be accessible in older browsers.

In this lesson, we will learn how add vendor prefixes to make flexbox accessible in as many browsers as possible. Then we'll use a tool called Grunt to concatenate and minify our files to make sure they load super fast. This will provide us a chance to discuss PostCSS, a brave new world in which CSS is transformed with Javascript plugins.

Preparing a Web project for production can be quite time-consuming, but luckily a lot of tedious tasks can be automated. I hope this lesson makes your workflow faster, more robust, and more efficient!

In this lecture, you can expect to:

Learn how to make your flexbox site and SVG images accessible for older browsers if needed.
Learn the benefits of using Grunt for Web production and how to install Grunt software and project files.
Learn how to use Grunt to minify CSS files, add browser prefixes and fallbacks for rem units, and optimize GIF, JPEG, and PNG images.
Explore some creative applications for SVG animation.

 

 

 

 

 

 

 

 

 

 

 

 

Adding browser prefixes can help you ensure your flexbox designs reach the widest possible audience.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To address older browsers, you can use prefixing tool like Autoprefixer or add alternate styles using Modernizr.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

You won't be able to use the mouse to move the cursor within Terminal or Command Prompt. Everything is done on the keyboard.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Sudo stands for "super user do," indicating you have administrator privileges on your computer.

 

 

 

 

If you don't use the sudo command to run a global update, you might get an error message containing the letters EACCES indicating you don't have permission to make the update.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Locally installed packages are only available for use within the context of your project, but globally installed packages can be used from anywhere on your machine.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Minified files typically follow the naming convention of "name.min.js", while the expanded version of the file will be "name.js".

Browser Support for Flexbox

 

You're ready to build your new Web site using flexbox—but is the world ready for it? I would say yes! Browser support for flexbox is very good. As of June 2018, flexbox is supported by all major browsers.

Green means go! The browser usage statistics from caniuse.com for flexbox look very positive.

Current data from caniuse.com show good support for flexbox, but you'll notice a few footnotes. Older versions of iOS safari and Android 4.3 only support flexbox with a -webkit- prefix. IE 10 requires an -ms- prefix. In fact, global support for flexbox is at 97.32% (prefixed), but only 96.32% (unprefixed). Not a huge difference, but you'll want to prefix your flexbox properties to reach the maximum possible audience.

Browser prefixes are written as "-webkit-flex" or "-ms-flexbox".

Before adding prefixes, my flexbox styles were ignored by Safari 8.03. The layout defaulted to a single column.

After adding prefixes, the flexbox styles were rendered correctly by the same browser.

You can add prefixes manually to your CSS, but the good news is that you don't have to. It is much more efficient to have an automated tool like Autoprefixer do the job for you. We'll be trying that out shortly. But first, let's decide what to do about IE8 and IE9. These versions offer no support for flexbox, even with prefixes.

According to current browser stats, IE8 usage is at 0.18% and IE9 usage is at 0.13%. That's about 3 people in every 1000 visitors. You'll have to analyze the demographics for your Web site to decide whether supporting IE8 and IE9 is worth it. Here are some facts to keep in mind as you evaluate your decision:

 
 
  • In January 2016, Microsoft ended support for IE 8, 9, and 10, encouraging its users to upgrade to IE11 or transition to the Edge browser on Windows 10.

  • Google no longer supports IE 8 or 9.

  • The latest version of Bootstrap 4 dropped support for IE8.
 
 

If your client absolutely requires support of IE 8 and IE 9, you can add alternate styles for these browsers using Modernizr. Choose "Flexbox" from the list of browser features and click "Build".

Modernizr will automatically detect browser support for flexbox in your user's browser.

Download the "Build" version of the modernizr.js file to your computer and add it to your product folder.

Add a link to the modernizr.js file in your HTML file.

The Modernizr script detects whether the browser supports flexbox. If flexbox is supported, Modernizr adds a class of "flexbox" to the <html> tag. If flexbox is not supported, Modernizr adds a class of "no-flexbox". You can use the "no-flexbox" selector to create alternate styles for older browsers.

In this example, the links display horizontally thanks to flexbox's "display: flex" property. I'll add a backup style setting the list items to "display: inline-block", creating the same layout in older browsers.

Of course, duplicating layout styles for older browsers is no one's cup of tea. If you must support IE 8, my advice is NOT to use flexbox for the overall page layout. Save flexbox for small page components (where you can easily add a few alternate styles) or as progressive enhancement (where flexbox improves the page design in compliant browsers, but doesn't cause problems in older browsers).

Hopefully, you won't have to provide fallbacks for IE 8 in every project. I'm sure there are a million other things you'd rather do, like reorganize your sock collection or take your dog for a walk.

Browser Support for SVG

Browser support for SVG images is even stronger than it is for flexbox. You can sleep soundly at night using SVG.

SVG images are supported by every major browser except for IE8.

If you are concerned about IE 8 in this case, I'd recommend adding a text description of each inline SVG image. In the worst case scenario, the description text will appear in place of the image. The <desc> text should go directly after the <title> tag. Note that the <title> tag is already embedded in the image—it is the same title as your Illustrator file. Using a clear, descriptive file name in Illustrator will create a clear, descriptive title for your SVG.

Your <title> and <desc> text will be readable by screen readers. To further improve accessibility, you'll also want to add the aria-labelledby attribute to ensure that screen readers recognize the <title> and <desc> as accessible labels for the SVG content.

Grunt

Before you launch a website, you'll likely need to perform several tasks to optimize your site performance:

 
 
  • Minify the CSS files

  • Add prefixes for older browsers

  • Optimize GIF, JPEG, and PNG images
 
 

Grunt is a task runner than can execute all of these tasks (and many more) with a single command. Prepare to be amazed!

Grunt is free and gives you access to a zillion free open-source plugins.

Install Node.js

Before you can use Grunt, you'll need to install Node.js. Head to https://nodejs.org/ and download the installer for your operating system. Choose the version that is "Recommended For Most Users".

Node.js includes npm, which you'll use to install your Grunt plugins.

Double-click the installer icon and you're good to go! Node.js includes npm, a popular package manager. However, npm is a separate project from Node.js, and tends to update more frequently. As a result, you’ll probably need to update your npm using the command line.

What's the command line? If you are as old as I am, you might remember running a computer by typing in commands. You booted up your computer and saw a prompt. You had to type commands to launch programs, run utilities, or change directories.

In the 1980s, people started wearing leg warmers and computers got dressed up with Graphical User Interfaces. These days, you open programs by clicking graphic icons. However, the command line is still used for system administration, computer programming, and batch processing.

If you haven't used the command line before, I understand why you might be hesitant. It seems like something better left to programmers—you're more of a designer type. However, many design utilities are run using the command line. If you want to customize your build of Bootstrap, you'll need to use the command line. If you want to use a preprocessor like Sass, you'll need to use the command line. If you want to set up a local server for testing and development, you'll need to use the command line. The command line is an important skill for designers to learn.

So, let's try it out! On Macs, the program used to work with the command line is called "Terminal". On Windows, it's called "Command Prompt".

Mac Users: Search for "Terminal" using your Launchpad.

Windows Users: Click Start, click All Programs (All apps in Windows 10), and then click Accessories (Windows System on Windows 10). You may need to right-click Command prompt, and then click Run as administrator depending on your user setting. If the User Account Control dialog box appears, confirm that the action it displays is what you want, and then click Continue.

Your terminal will look something like the screenshot below. No bells and whistles here, just plain text.

The text on the left displays your current location in the file system. I'm currently at the top level of my home directory (represented as "~").

The input area on the right with the blinking cursor is "the prompt". On Macs, the prompt is marked by a $ sign. On Windows, you'll see a > sign.

Enter the following command after the prompt and hit your Return key.

sudo npm install npm@latest -g

You are asking the terminal to update npm globally on your computer (the -g flag means "global"). Global updates require administrator privilege, so you started your command with "sudo" (super user do). Windows users: you do not need to type the "sudo" command. Simply type npm install npm@latest -g

After typing a sudo command, the terminal will spin its wheels for a second (it's a little slow on my computer anyway) then prompt you for a password. Enter your administrator password (the same password that you use to log into your computer and perform software updates) and hit Return.

The terminal will take a second to accept your password and process the results. A copy of npm will be installed in your local library. Done!

To wrap up, the terminal presents a new prompt, ready for your next command.

Install Grunt (Globally)

You're ready to install Grunt's command line interface (CLI) globally on your computer. Enter the following command after the prompt and hit your Return key.

sudo npm install -g grunt-cli

This is a global installation and you are using the "sudo" command, so you'll need to enter your administrator password.

Windows users: type npm install -g grunt-cli

Project Set-Up

Follow along as we try out Grunt on an actual project. Download the grunt-boilerplate folder. Unzip the file and drag it to the desktop of your computer. The folder contains an HTML file, a custom.css file, a few image files, and a few SVG files.

We'll need two additional files in order to run Grunt in this project.

 
 
  • package.json is a manifest file that contains information about the project and a list of all the plugins that it uses (aka "dependencies").

  • Gruntfile.js is used to configure tasks and load Grunt plugins. It tells Grunt what it needs to do.

 
 

Create a package.json

Open your code editor and create a new document. Choose JSON from the list of file types. In Dreamweaver, the New Document window looks like this:

If you are using Dreamweaver, delete the //JSON Document comment. It will cause an error when you run Grunt.

Copy and paste the following code into your JSON file. This is the standard configuration from the "Getting Started" page at gruntjs.com.

{
"name": "my-project-name",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0"
}
}

Change the name of the project to "grunt-boilerplate". Delete all of the dependencies — we will be using a different set of plug-ins for our project. Your file will look like this when you're done.

Save your file as "package.json" at the root level of the grunt-boilerplate folder.

Create a Grunt.js

We need one more file to set up our project: the Gruntfile. Create a new document using your code editor. Choose the JavaScript file type.

Delete the // JavaScript Document comment (if there is one).

Copy and paste the following code into your file. This code is provided on the "Getting Started" page at gruntjs.com (an example Gruntfile).

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %>
<%= grunt.template.today("yyyy-mm-dd") %> */\n'
}, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']); };

Your file should look like this:

Save your file as "Gruntfile.js" at the root level of the grunt-boilerplate folder.

Install Grunt (Locally)

You've installed the Grunt command line globally, which is a one-time deal—you won't need to do that again. However, each time you set up a project, you'll need to install Grunt locally in the project folder.

Open your terminal and navigate to your project folder. Navigating folders using the terminal can be a bit tricky, but here's how I do it. Type "cd" (change directory) into the terminal and hit your Return key.

cd 

This brings you up to the top level of your user directory. Even if you were already there, it's good to know where you are starting from.

Now type "cd desktop/grunt-boilerplate" and hit Return.

cd desktop/grunt-boilerplate

This will navigate to the grunt-boilerplate folder on your desktop.

If you have trouble finding your folder in the directory structure, a cool trick is to type "cd" and a space mark, then drag and drop the folder from your desktop into the prompt. Hit Return, and the terminal will write the path to the folder.

OK! Now that you are in the right place (make sure the path in your terminal says "grunt-boilerplate"), type the following command and hit Return.

npm install grunt --save-dev

Wait for the terminal to process the request. You might get a few warnings about missing fields in the JSON document, but don't worry, these fields aren't really necessary.

When the terminal is finished, you'll have a new node_modules folder inside your grunt-practice folder.

The terminal has installed Grunt and saved it as a dependency. Open your package.json file to check—there it is!

Grunt Task #1: postcss

The grunt-postcss plugin is a package deal. It generates pixel fallbacks for rem units, adds browser prefixes (we will need prefixes for our flexbox styles and CSS transitions) and minifies the CSS file so it is faster to load. Excellent!

Reference grunt-postcss on npm to customize the set-up.

grunt-postcss has its own npm page with information on how to install and customize the plugin.

Follow this 5-step process:

1. Install the plugin.

Type the following command into your terminal and hit Return.

npm install grunt-postcss --save-dev

One more to go. Type the following command and hit Return.

npm install pixrem autoprefixer cssnano --save-dev

The new dependencies will be added automatically to your package.json.

2. Enable the plugin in your Gruntfile.

Open your Gruntfile.js file. Look for the // Load the plugin that provides the "uglify" task comment.

Add this line of JavaScript:

grunt.loadNpmTasks('grunt-postcss');

Select the uglify plugin and hit Delete.

Your code should look like this:

3. Configure the plugin in your Gruntfile.

Take a look at the code under "Project configuration". Hit your Return key to create an empty space before "uglify". Add another space after uglify's closing bracket }.

Here's how the code is structured. This code outlined in red is the configuration code for the uglify plugin. The code highlighted in blue wraps around the configuration code for all the plugins that we insert into the document.

The uglify plugin is used to minify JavaScript files, but we are not using JavaScript in this project. Select the uglify configuration code and hit Delete.

Copy the following postcss configuration code:

postcss: {
options: {
processors: [
require('pixrem')(), // add fallbacks for rem units
require('autoprefixer')({
browsers: 'last 2 versions'
}), // add vendor prefixes
require('cssnano')() // minify the result
]
},
dist: {
src: 'css/custom.css',
dest: 'css/custom.min.css',
}
}

Edit > Paste the postcss configuration code:

4. Add the plugin to the task list.

Change the task list to 'postcss' rather than 'uglify'.

5. Run Grunt!

Save your Gruntfile.js and open your terminal. Type the command "grunt".

grunt

After a short delay, the terminal should tell you "1 processed stylesheet created. Done."

Grunt has created a new custom.min.css file in the css folder.

The custom.min.css file is minified, so all the styles are on a single line. If you look closely, you'll see the browser prefixes.

To load the minified style sheet, open the index.html file and change the custom.css link to custom.min.css.

Grunt Task #2: imagemin

Images are often the largest assets in a website. Optimizing your images will greatly improve your page load time. The grunt-contrib-imagemin plugin will optimize PNG, JPG, and GIF images in an instant.

There are a few sample images in the images folder in the grunt-boilerplate folder. Duplicate the images folder so you have a back-up in case anything goes wrong with the optimization process. You can call the duplicate folder "images-original".

1. Install the plugin.

With your terminal pointed at the "grunt-boilerplate" directory (you should be there already) type the following command and hit Return.

npm install grunt-contrib-imagemin --save-dev

2. Enable the plugin in your Gruntfile.

Open your Gruntfile.js file. Look for the comment that says // Load the plugin that provides the "uglify" task.

Add this line of JavaScript after the grunt-postcss JavaScript:

grunt.loadNpmTasks('grunt-contrib-imagemin');

Your code should look like this:

3. Configure the plugin in your Gruntfile.

Copy the following imagemin configuration code:

imagemin: {
dynamic: {
files: [{
expand: true,
cwd: 'images/',
src: ['**/*.{png,jpg,gif}'],
dest: 'images/'
}]
}
}

Add a comma after the postcss configuration code (I've outlined the comma in green so you can see where it should go). This is an important detail – without the comma it won't work!

Hit Return to create an empty space, then paste the imagemin configuration code. It should look like this:

4. Add the plugin to the task list.

Add 'imagemin' to the Default task(s) array, separated by a comma.

5. Run Grunt!

Save your Gruntfile.js. Open your terminal and type the command "grunt".

grunt

The terminal will inform you that it minified 5 images (saving 704 kB - 7.7%). Not bad!

I hope your Grunt project was a success. If it didn't work, don't be frustrated, I couldn't get Grunt to process my first attempts either. Start over from the beginning, making sure that you've installed all the plugins correctly. Check your Gruntfile.js and package.json files against my files for accuracy. If you are still stuck, contact your instructor for help.

Leveling Up with Grunt

We've installed two Grunt plugins, which is awesome! We've added browser prefixes, minified our CSS file, and optimized our images. You should perform these basic tasks before going into production on any project. However, there are many, many more plugins that you can add to your Gruntfile.

To date, there are 6,411 plugins in the Grunt directory. Which ones will come in handy in your projects?

Learning Grunt is a challenge at first, but once you understand the theory of how plugins work, it's a snap to set up. Here's a list of resources to review what you know and fill in the gaps of what you don't know.

 
   
 

0% represents the beginning state of the animation. 100% represents the ending state of the animation.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SVG Animation Using CSS3

Browser prefixes, minification, and optimization are necessary tasks, but I'd like to end this course on a more exciting note.

There are two ways to animate SVG images using CSS: the transition property and the animation property.

In Lesson One, you learned how to use the transition property. We changed the color, size, and rotation of the leaf on hover.

Click the diagram to view the demo.

The transition property slows the leaf's transformation from pale-green to dark-green—the color gradually eases in as you mouse over the logo. These styles handle the color changes inside the leaf:

And these styles control how the entire leaf rotates and scales:

In the example below, I've created the same effect using the animation property rather than the transition property. It looks exactly the same, with one important difference—the animation plays automatically when you launch the page in a browser. You don't have to hover over the logo to trigger the animation.

Click the diagram to view the demo.

Your decision on whether to use the transition or animation property will depend on whether you want to trigger the animation with a user behavior (like a mouse over) or whether you want the animation to play automatically. Hover interactions are nice because they are unobtrusive. The animation only plays when the user interacts with the logo by mousing over it. If your site visitor is busy reading the other page content, he or she won't be distracted by a flashing, animating logo. On the flip side, hover interactions don't work on touch devices like iPhones and iPads, so the animation shouldn't be essential.

If you use the animation property, the animation will play automatically on ALL devices. Carefully planned, you can create animations that add meaning and context to your design, and some fun!

CSS animations have two components.

 
 
  • Keyframes: Keyframes define what happens in the animation (for example, the element will change color or grow in size).

  • Animation Properties: The animation properties define how the animation will occur (for example, the animation will have a duration of 1.5 seconds and repeat 3 times).
 
 

I created two animations for the leaf example. The "green" animation changes the color of the inner leaf:

The "grow" animation rotates and scales the leaf:

You can create more complex animations by adding additional keyframes. Here I've defined a different color for each stage of the animation timeline.

And a different rotation and scale factor:

The leaf changes from green to blue to pink to orange as it rotates and spins in different directions.

Click the diagram to view the demo.

The keyframes determine what happens, but the animation properties determine how it will happen. The animation shorthand allows to you define all the animation settings in one line of code.

animation: [animation-name] [animation-duration] [animation-timing-function]
[animation-delay] [animation-iteration-count] [animation-direction]
[animation-fill-mode] [animation-play-state]

Start by defining the animation-name and animation-duration (these properties are required for the animation to run). Any additional settings are optional.

 
 
  • [animation-name] This is the name of the animation that you defined in the keyframes (you can name your animation anything you want).

  • [animation-duration] The total run time, in seconds or milliseconds.

  • [animation-timing-function] The speed curve of the animation.

    "Ease" is the default timing-function, but there are several to choose from:

    • ease: Starts slow, speeds up, and slows at end.

    • ease-in: Starts slow, then speeds up.

    • ease-in-out: Similar to ease, but slower at each end.

    • ease-out: Starts fast, and slows towards the end.

    • linear: Constant speed throughout.

    • cubic-bezier(x1,y1,x2,y2): Specifies a cubic-bézier curve to control the speed.

  • [animation-delay] The delay before the animation starts. If you set the animation-delay to 5s, the animation will start to play 5 seconds after it is triggered.

  • [animation-iteration-count] This is a "loop" setting that determines the number of times the animation will play. The default setting is "1" — your animation will play once. If you change the animation-iteration to "5", it will play five times. If you change the animation-iteration to "infinite", it will repeat forever.

  • [animation-direction] The direction that the animation runs.

    • normal: On each cycle the animation resets to the beginning state (0%) and plays forward again (to 100%). This is the default setting.

    • reverse: On each cycle the animation resets to the end state (100%) and plays backwards (to 0%).

    • alternate: On each odd cycle, the animation plays forward (0% to 100%). On each even cycle, the animation plays backwards (100% to 0%).

    • alternate-reverse: On each odd cycle, the animation plays in reverse (100% to 0%). On each even cycle, the animation plays forward (0% or 100%).

  • [animation-fill-mode] The animation-fill-mode determines what you see before and after the animation.

    • normal: The animation does not apply any styles to the element before or after the animation. This is the default setting.

    • backwards: Before the animation happens, the styles of the initial keyframe (0%) are applied to the element.

    • forwards: After the animation is finished, the styles of the final keyframe (100%) remain in effect.

    • both: Before the animation, the styles of the initial keyframe (0%) are applied to the element. After the animation, the styles of the final keyframe (100%) are remain in effect.

  • [animation-play-state] You have two options here: playing or paused.

    • playing: The animation plays.

    • paused: The animation pauses.
 
 

If you'd like to experiment with these settings, download the svg-animation folder and give it a go!

Case Studies in SVG Animation

SVG animation has the support of all the major browser manufacturers (including IE9), so expect to see more of it. You can use CSS to animate buttons:

SVG Animation by Sean McCaffery

Create interactive diagrams (click to explore the Trajan column):

wikimedia

Create info-graphics:

'Creepy mouth' by Doug Schepers

Create cool type effects:

Animated writing font by Lee Porter

Animate loading icons:

SVG Loading icons by Aurer

And generate full-fledged animations:

SVG Bouncy Car by Chris Gannon

If you are interested in Web animation, now is a great time to learn more about SVG graphics!

Discussion
Share your thoughts and opinions on postCSS in the Discussion area.

Exercise
Put what you've learned to the test by running Grunt on your bike shop website.