Posts tagged code
– page 3

New! A Webpack plugin for optimizing Google AMP CSS

In a previous post, I went over my process trying to develop a PostCSS plugin to make updating custom CSS for Google AMP easier. That plugin seems to be working okay (yay!) but since it’s not integrated into my Webpack build, there’s still a lot of manual work to be done and room for human error. 

So the next step was clearly to jump into learning Webpack plugins. I needed a plugin that could watch for CSS files created using the MiniCssExtractPlugin, filter out all the unnecessary styles, and create a new file just for AMP. The results are on GitHub

My build process is pretty simple so it’s very likely there are options or interactions I haven’t thought of. If you end up using it and notice any problems, let me know!   

Optimizing CSS for Google AMP

I have mixed feelings about AMP but supporting it seems like the only way to get any traction in Google searches for my fonts so here we are.

When I was updating my site less frequently, manually copying and pasting the stylesheets and cleaning them up for AMP seemed perfectly reasonable. Lately though, I’ve been trying out new designs more often and it’s been too easy to accidentally forget and let the pages get out of sync. So I decided to try creating a PostCSS plugin to automate the process.

Thinking about the changes I was making by hand, I knew I wanted my plugin to filter out:

  • Media queries for desktop breakpoints
  • Non -webkit- vendor prefixes
  • Specific block names or other prefixes (for BEM or other namespacing)

It also seemed like a good idea to fix or remove styling the AMP docs mention as being invalid:

  • !important flags
  • -amp classes or i-amp tags

The PostCSS guidelines are pretty clear that a plugin should “do one thing, and do it well” and provide some examples using monorepos to handle more complex tasks. I’ve been meaning to work more with Lerna so this seemed like a good excuse. 

The result is the postcss-amplify plugin. You can check out Github for instructions on how to use it with your current PostCSS setup or I made a web interface for quick use. If you try it and notice anything weird, let me know in the comments or by submitting an issue. I think my next step will be to create a Webpack plugin to fully automate my build so keep an eye out for that.

React Inner Image Zoom, an Open Source Component so I Can Stop Writing the Same Code All the Time

Apologies if you were hoping for a font related post but I just released my first open source React component on NPM so I’m pretty excited about that right now.

Maybe it’s because I’ve done a lot of e-commerce work but it feels like I’ve had to implement the same product image zoom in multiple occasions. This jQuery Zoom plugin sufficed for a while but it usually required some retooling for mobile devices and didn’t feel right in an otherwise React environment. Not to mention the issues that cropped up when the designs called for image zooming inside a carousel (which was surprisingly often). So I tried to take that zoom and pan functionality and move it over into a React component that works nicely with other React modules. 

React Inner Image Zoom includes:

  • Default zoom on click + pan on hover behavior
  • Zoom on click + drag on move on touch devices
  • Support for responsive images
  • Optional fullscreen zooming at smaller breakpoints on touch devices
  • Out of the box integration with other React components like react-lazyload and react-slick

I have a demo page up here and the Github repo has more details on how to use it. Figuring out the build process for open-sourcing a React component one of the trickier parts so let me know if there are any installation problems (or problems of any kind really).  

Oh Hey! My First NPM Package

I just published my first NPM package!

It’s a fairly simple module to add a height based JavaScript fallback for -webkit-line-clamp on browsers that don’t support it (i.e., Firefox and IE). I came across a case at work where we needed cross-browser line clamping with different line counts depending on the breakpoint and with consistent rendering even with web fonts on mobile devices. We tried a few existing JS truncation libraries but sometimes you want the CSS to do the heavy lifting.

The module is called Clamps (because my names are often Futurama inspired) and you can check it out on NPM or Github.

Hit me up with all your issues; they’re still new and exciting to me!

Using the will_paginate gem to generate a React renderable HTML string

Generally when I post about programming it’s because I’ve finally found a solution to something that has been driving me crazy, some problem with a lot of documentation to read through and not a lot of Stack Overflow answers (and if they do exists, the use case is always a little off). This is no exception!

First, why would you want to combine React and Ruby gems? In my case, I thought it was the simple, lazy path. Way back when, the site I work for was built using will_paginate for all pagination. I recently ran into a case where I needed to include that pagination in a React app. It wasn’t for anything fancy, the links and logic needed to match the rest of site. So, trying to re-create the gem magic in React seemed like overkill compared to creating a partial with the code, generating an HTML string using the Rails render_to_string method, and sending it to React to dangerouslySetInnerHTML.

Well, it turns out will_paginate uses the url_for method and it was not happy to be called without the request data (undefined method `host’ for nil:NilClass?!) it uses to build urls. After a lot of hunting around, I came across this pull request to get will_paginate working with mountable engines that seemed adaptable. My helper accepts the request from the controller and pulls out the necessary fields to send with it’s own instance of url_for. 


module WillPaginateHelper
  class StringLinkRenderer < WillPaginate::ActionView::LinkRenderer
    protected

    def url(page)
      @base_url_params ||= begin
        url_params = merge_get_params(default_url_params)
        url_params[:only_path] = true
        merge_optional_params(url_params)
      end

      url_params = @base_url_params.dup
      add_current_page_param(url_params, page)

      if @options[:url_builder]
        @options[:url_builder].call(url_params)
      else
        @template.url_for(url_params)
      end
    end
  end

  def will_paginate_string(collection = nil, options = {}, request = nil)
    params = {
      :host => request[:host],
      :controller => request[:controller],
      :action => request[:action]
    }

    unless request.query_parameters.empty?
      request.query_parameters.each do |k, v|
        params[k] = v
      end
    end

    options.merge!(
      :renderer => WillPaginateHelper::StringLinkRenderer,
      :url_builder => ->(params) { Rails.application.routes.url_for(params) },
      :params => params
    )

    will_paginate(collection, options)
  end
end

(GitHub Gist)

Then, in the partial that is being rendered to a string, it’s possible to call will_paginate_string(collection, options, request) and the results can be sent as a JSON response to render in React.

Admittedly, Rails isn’t my strong suit so I’d be interested to hear if I missed some easier way to do this.

Asset Manifests with Swig Templates

I’m still using the Swig templating engine for my Node/Express apps and recently decided to streamline my asset caching process. I wanted a simple way to insert hashed assets into my templates but everything that Google came up with seemed a little too complicated. So I wrote a quick custom Swig tag to read from an asset manifest (in my case public/assets.json generated by the Webpack Manifest Plugin):

let assets = require('../../public/assets.json');

module.exports = function (swig) {
  swig.setTag('asset', _parse, _compile, false, true);
};

let _parse = function (str, line, parser) {
  parser.on('*', function (token) {
    let match = token.match.match(/^["'](.*?)["']$/);
    let assetPath = match ? match[1] : null;

    if (assetPath) {
      let asset = assets[assetPath] || assetPath;
      this.out.push(asset);
    }
  });

  return true;
};

let _compile = function (compiler, args) {
  if (!args || !args[0]) {
    throw new Error('The asset tag expects a filename as a string');
  }

  let assetPath = args[0].startsWith('/') ? args[0] : '/' + args[0];
  return `_output +="${assetPath}";`;
};

(GitHub Gist)

I keep this file in an app/helpers directory and in app.js my views are set up with the lines:

require('./app/helpers/assets')(swig);
app.engine('html', swig.renderFile);
app.set('view engine', 'html');

After that stylesheets or JavaScript can be added to any template using the unhashed name like so:

<script src="{% asset 'main.js' %}"></script>
<link rel="stylesheet" href="{% asset 'main.css' %}" />

Using Bookmarklets for Basic Chrome for iOS Debugging

So I was trying to figure out a design bug that could only be recreated in the Chrome browser on an iPhone and it was kind of a hassle. Safari on iOS and the Chrome DevTools emulator looked fine so they were no help. Xcode’s simulator and remote debugging only work with Safari so they were out too. I know there are other resources you can either pay for or take the time to install that might get the job done but I really only needed to test a couple lines of CSS.

I remembered from years back that Firebug Lite could run on any browser or mobile device but it’s actually a little heavy and hard to use on a small screen. But — looking it up — I had the epiphany that if you know JavaScript there’s no reason you can’t just make your own bookmarklet for debugging. This might be totally obvious but it took a while to occur to me so there you go.

If you want to do a really quick styling update, all you need is something like this (note: this is not the issue I was trying to fix):

javascript:(function () {
    var el = document.querySelector('body');
    el.style.color = '#fff';
    el.style.backgroundColor = '#000';
})();

Or, if you want to insert an external file:

javascript:(function () {
    var src = 'path/to/styles.css';
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = src;
    document.head.appendChild(link);
})();

Then you just open Chrome and create a dummy bookmark, select edit, and replace the URL field with your JavaScript. You can also rename the bookmark to something more accurate if you want.

For more information, TutsPlus has a useful tutorial on creating bookmarklets.