Table of Contents for
Mastering PostCSS for Web Design

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Mastering PostCSS for Web Design by Alex Libby Published by Packt Publishing, 2016
  1. Cover
  2. Table of Contents
  3. Mastering PostCSS for Web Design
  4. Mastering PostCSS for Web Design
  5. Credits
  6. About the Author
  7. About the Reviewer
  8. www.PacktPub.com
  9. Preface
  10. What you need for this book
  11. Who this book is for
  12. Conventions
  13. Reader feedback
  14. Customer support
  15. 1. Introducing PostCSS
  16. Introducing PostCSS
  17. Setting up a development environment
  18. Creating a simple example using PostCSS
  19. Linting code using plugins
  20. Exploring how PostCSS works
  21. Summary
  22. 2. Creating Variables and Mixins
  23. Creating a hover effect example
  24. Transitioning to using PostCSS
  25. Adding variable support to PostCSS
  26. Updating our hover effect demo
  27. Setting the order of plugins
  28. Creating mixins with PostCSS
  29. Looping content with PostCSS
  30. Summary
  31. 3. Nesting Rules
  32. Navigating through pages
  33. Transitioning to using PostCSS plugins
  34. Exploring the pitfalls of nesting
  35. Making the switch to BEM
  36. Exploring our changes in more detail
  37. Summary
  38. 4. Building Media Queries
  39. Exploring custom media queries in PostCSS
  40. Making images responsive
  41. Adding responsive text support
  42. Optimizing media queries
  43. Retrofitting support for older browsers
  44. Moving away from responsive design
  45. Taking things further with CSS4
  46. Summary
  47. 5. Managing Colors, Images, and Fonts
  48. Managing fonts with PostCSS
  49. Creating image sprites
  50. Working with SVG in PostCSS
  51. Adding support for WebP images
  52. Manipulating colors and color palettes
  53. Creating color functions with PostCSS
  54. Summary
  55. 6. Creating Grids
  56. Creating an example with Bourbon Neat
  57. Exploring the grid plugins in PostCSS
  58. Transitioning to using PostCSS-Neat
  59. Creating a site using Neat and PostCSS
  60. Adding responsive capabilities
  61. Summary
  62. 7. Animating Elements
  63. Moving away from jQuery
  64. Making use of pre-built libraries
  65. Switching to using SASS
  66. Making the switch to PostCSS
  67. Exploring plugin options within PostCSS
  68. Updating code to use PostCSS
  69. Creating a demo in PostCSS
  70. Optimizing our animations
  71. Using our own animation plugin
  72. Summary
  73. 8. Creating PostCSS Plugins
  74. Dissecting the architecture of a standard plugin
  75. Creating an transition plugin
  76. Building a custom font plugin
  77. Simplifying the development process
  78. Guidelines for plugin building
  79. Making the plugin available for use
  80. Summary
  81. 9. Working with Shortcuts, Fallbacks, and Packs
  82. Exploring plugin packs for PostCSS
  83. Adding shortcuts with Rucksack
  84. Linting and optimizing your code
  85. Providing fallback support
  86. Summary
  87. 10. Building a Custom Processor
  88. Exploring our processor
  89. Dissecting issues with our processor
  90. Optimizing the output
  91. Adding reload capabilities
  92. Extending our processor further
  93. Testing the final pre-processor
  94. Getting started with some hints and tips
  95. Introducing the CSStyle library
  96. Summary
  97. 11. Manipulating Custom Syntaxes
  98. Preparing our environment
  99. Implementing custom syntax plugins
  100. Parsing CSS
  101. Formatting the output with the API
  102. Highlighting our syntax code
  103. Summary
  104. 12. Mixing Preprocessors
  105. Exploring the conversion process
  106. Introducing the Pleeease library
  107. Compiling with other preprocessors
  108. Using the PreCSS library
  109. Converting a WordPress installation
  110. Setting up our environment
  111. Considering the conversion process
  112. Making changes to our code
  113. Compiling and testing the changes
  114. Summary
  115. 13. Troubleshooting PostCSS Issues
  116. Exploring some common issues
  117. Getting help from others
  118. Summary
  119. 14. Preparing for the Future
  120. Converting CSS4 styles for use
  121. Supporting future syntax with cssnext
  122. Creating plugins to provide extra CSS4 support
  123. Summary
  124. Index

Building a custom font plugin

For our next demo, we're not going to build something original, but start with adapting an existing plugin that is already available for PostCSS. The plugin we will use is postcss-fontpath by Seane King (available from https://github.com/seaneking/postcss-fontpath); we're going to incorporate an autocomplete facility that automatically adds the relevant font stack, based on the name provided, and using the lists available at http://www.cssfontstack.com/.

"Why do this", I hear you ask? To prove a point—it isn't always necessary to re-invent the wheel; sometimes it is preferable to simply adapt something that exists, which doesn't quite fit our requirements. In this instance, the code we're adding will make it more useful; it will need some further development to allow for error-checking, but nonetheless still serves a purpose.

Note

A point of note—recommended practice is to use the plugin boilerplate we covered in the previous section. For this next exercise, we will build it manually—this is to show you something of the process, even though it is not one we would release into the wild.

Okay, that aside, let's get stuck in and start developing our plugin:

  1. We'll start by creating a folder within the root of our project area—go ahead and name this folder postcss-custom-fonts.
  2. Next, we need to set up the folder as a Node module, so fire up a Node.js command prompt and change the working folder to our plugin folder.

    At the prompt, enter npm init to start the process of creating a package.json file—use the details shown in this screenshot, at the appropriate prompt:

    Building a custom font plugin
  3. With the prompt still open, go ahead and enter these commands, then press Enter—the first one is needed to install PostCSS as a dependency for our plugin, with the second installing underscore.js, as a second dependency (it's used for the extend method):
    npm install postcss --save
    npm install underscore --save
    

    Keep the session open—we will need it towards the end of this exercise.

  4. From the code download that accompanies this book, we need to extract a copy of index.js—copy this to the plugin folder.
  5. If all is well, we should see something akin to this screenshot when browsing the contents of our plugin folder:
    Building a custom font plugin
  6. With our plugin in place, let's test it out. For this, we need a couple of files from the code download that accompanies this book. Go ahead and extract copies of gulpfile.js and package.json from the T42 – Building a custom font plugin folder (and not the plugin one!), then save them to the root of our project area.
  7. In a new file, add the following CSS styles, saving it as style.css in the src folder in our project area:
    @font-face {
      font-family: 'robotoregular';
      font-path: '/fonts/Roboto-Regular-webfont';
      font-weight: normal;
      font-style: normal;
    }
    
    h1 { font-family: robotoregular,  fontstack("Extra Stack"); }
  8. Revert back to the Node.js command prompt session we had open at the start of this exercise. Make sure the working folder is set to our project area before continuing.
  9. At the prompt, enter gulp then press Enter—PostCSS will go away and compile the source style sheet. If all is well, we should see the compiled results of our style sheet in the dest folder of our project area:
Building a custom font plugin

At this stage, we now have a working plugin—even though this is not an original creation, it still highlights a number of key concepts around construction of PostCSS plugins. Let's take a moment to explore the functionality of our plugin in more detail.

Dissecting the functionality of our plugin

At first glance, the code for our plugin may look complex, but in reality it is relatively straightforward to follow—let's go through it in sections, beginning with defining instances of the postcss object and a fontstacks_config object we will use in the plugin:

var postcss = require('postcss');
var _ = require('underscore');

// Font stacks from http://www.cssfontstack.com/
var fontstacks_config = {
  'Arial': 'Arial, "Helvetica Neue", Helvetica, sans-serif',
  'Times New Roman': 'TimesNewRoman, "Times New Roman", Times,
  Baskerville, Georgia, serif'
}

Next up, we add a simple helper function—this is used to convert font names into title case; the names listed in fontstacks_config are case sensitive, and will fail if they don't match:

// Credit for this function: http://stackoverflow.com/a/196991
function toTitleCase(str) {
  return str.replace(/\w\S*/g, function(txt){
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
}

This is the start of the plugin—the first two lines are the obligatory initialization to make the plugin available for use, followed by defining an options object. We then use _.extend to extend the predefined values in our chosen font stack with those added to the configuration object when running the plugin:

module.exports = postcss.plugin('customfonts', function (options) {
  return function (css) {

    options = options || {};
    fontstacks_config = _.extend(fontstacks_config, options.fontstacks);

We then walk through each rule and node, working out if they first contain a font declaration, then if they contain a font name that matches one in the predefined font stacks. If there is a match, then the font name is converted to the appropriate font stack and inserted with any additional fonts specified, but which don't match our font stacks:

    css.walkRules(function (rule) {
      rule.walkDecls(function (decl, i) {
        var value = decl.value;
        if (value.indexOf( 'fontstack(' ) !== -1) {
          var fontstack_requested = value.match(/\(([^)]+)\)/)[1].replace(/["']/g, "");
          fontstack_requested = toTitleCase(fontstack_requested);

          var fontstack = fontstacks_config[fontstack_requested];
          var first_font =  value.substr(0, value.indexOf('fontstack('));

          var new_value = first_font + fontstack;
          decl.value = first_font + fontstack;
        }
      });
    });

In the second half of the plugin, we perform a simpler task—we work our way through each rule and declaration, looking for any instances of @font-face in the code. We then define a fontpath variable that removes any quotes from the supplied values, and a format array to manage the different font formats available for use:

    css.walkAtRules('font-face', function(rule) {
      rule.walkDecls('font-path', function(decl) {
        var fontPath = decl.value.replace(/'/g, ''),
        src = '',
        formats = [
          { type: 'woff', ext: '.woff' },
          { type: 'truetype', ext: '.ttf' },
          { type: 'svg', ext: '.svg' }
        ];

We then build up the relevant statement for each font type, before assembling the custom font declaration and inserting it back into the appropriate point in our style sheet:

        formats.forEach(function(format, index, array) {
          if (index === array.length - 1){
            src += 'url("' + fontPath + format.ext + '")
        format(\'' + format.type + '\')';
          } else {
            src += 'url("' + fontPath + format.ext + '")
        format(\'' + format.type + '\'),\n ';
          }
        });

        decl.cloneBefore({ prop: 'src', value: src });
        decl.remove();
      });
    });
  }
});

Our plugin has exposed some key concepts in PostCSS plugin design—the main ones are the use of .WalkDecls and .WalkRu les (or .WalkAtRules). I would strongly recommend familiarizing yourself with the API documentation at https://github.com/postcss/postcss/blob/master/docs/api.md, which outlines all of the commands available within the API, and gives a brief description of their purpose.

Despite creating what should be a useful plugin, it isn't one that I would recommend releasing into the wild. At this point you may think I have completely lost the plot, but as I always say, "there's method in the madness"—there are good reasons for not publishing this plugin, so let's take a moment to explore why it might not be a sensible move to release this plugin in its current format.

Exploring the dangers of publishing this plugin

Over the last few pages, we've created what should be a useful plugin to manipulate custom fonts—it automatically builds up the right font stack based on pre-defined settings, and will fill in the appropriate @font-face code for us. At this point we should have a plugin that can be released into the wild, for anyone to use…surely?

Well yes, and no—even though this plugin serves a purpose, it is not one that I would recommend making available…at least not yet! There are a few reasons why, which also help to illustrate the benefits of using the boilerplate code we covered earlier in this chapter:

  • The plugin doesn't have a test.js file or configuration associated with it—one of the requirements for releasing plugins is that each be tested, using a test.js file. Ideally we might use a service such as Travis CI to help with this, but this really only works if you use a Unix-based environment for development.
  • The plugin itself performs two different roles, which is not recommended—best practice for PostCSS plugins is to try where possible to limit the role to one task only. The benefits of this can be seen when using a task runner—we can pick and choose which plugins to use, without introducing too much extra unwanted functionality.
  • The architecture of our code is not optimal—this is primarily due to the use of css.WalkRules (line 16), and css.WalkAtRules (line 28). These two commands parse each node within the container, and call the callback function for each rule node and at-rule node. The difference here is that css.WalkRules works on every rule; css.WalkAtRules will only work on @-rules (such as @font-face). They are not interchangeable, which makes it very inefficient at compilation.
  • If we don't use the plugin boilerplate, then most of the files required for publishing code will not be present—these either have to be created by hand, or created as part of submitting to GitHub. If we use the boilerplate, then this will be done for us automatically, along with configuring the package.json file for us—all we need to do is add a suitable task runner such as Gulp or Grunt.

One might ask why we would even consider this route, if it is likely to throw up issues during development—the simple answer is that it helps us understand something of how plugins should be built. If we're building a plugin for personal use only, then there is no need for some of the files or processes that we have to use when releasing plugins for general use.