Categories
Scripts

Using criticalCSS with Gulp.js

I have a site that is using Gulp.js as its task runner. I wanted to use criticalCSS on the site and because there isn’t a Gulp plugin quite yet, I had to roll it myself.

I’m going to try and tackle writing the plugin myself, but for the time being, here is a quick script to get it running:

gulp.task('critical', function() {
  var request = require('request');
  var path = require( 'path' );
  var criticalcss = require("criticalcss");
  var fs = require('fs');
  var tmpDir = require('os').tmpdir();

  var cssUrl = 'http://localsite.dev/wp-content/themes/theme_name/style.css';
  var cssPath = path.join( tmpDir, 'style.css' );
  var includePath = path.join( __dirname, 'inc/critical.css.php' );
  request(cssUrl).pipe(fs.createWriteStream(cssPath)).on('close', function() {
    criticalcss.getRules(cssPath, function(err, output) {
      if (err) {
        throw new Error(err);
      } else {
        criticalcss.findCritical("http://localsite.dev/", { rules: JSON.parse(output) }, function(err, output) {
          if (err) {
            throw new Error(err);
          } else {

            fs.writeFile(includePath, output, function(err) {
              if(err) {
                return console.log(err);
              }
              console.log("Critical written to include!");
            });

          }
        });
      }
    });
  });

});

As you’ll probably notice, this is a WordPress site, but you can tweak some of the parameters in here to meet your needs. I have the inlined css outputting into an include called critical.css.php. You can reference my post on Using criticalCSS in WordPress on how to output this include in your <head>.

Categories
Scripts

Using criticalCSS in WordPress

I’ve been obsessed with optimizing websites for awhile, and one big hurdle to a faster website is “render-blocking” JavaScript and CSS. This came up as I was trying to get a high score on Google PageSpeed Insights.

Screenshot of Render Blocking Error in PageSpeed Insights | "83/100 Speed. Consider Fixing: Eliminate render-blocking JavaScript and CSS in above-the-fold content"

Google recommends you move these scripts and css files to the bottom of the DOM (just before the </body>). With CSS, it’s a little more complicated because you have to figure out the “above the fold” (ugh hate that term) styles and inline them in the <head>. More on that in the criticalCSS section.

Assumed knowledge of enqueuing

This post assumes knowledge of enqueuing scripts/styles in WordPress. If you haven’t used these functions in WordPress, I’d highly recommend brushing up on them. Regardless of if you want to use criticalCSS or not, this is a much better way to insert styles/scripts into a theme. Here are some good resources:

Moving JavaScript to bottom of the DOM

Luckily, WordPress has built-in mechanisms to put your script at the bottom of the DOM. The 5th parameter of the wp_enqueue_script() function is a boolean $in_footer. which tells WordPress whether or not to put it in the footer. Here’s a quick example inside your functions.php:

wp_enqueue_script( 'mysite_main', get_template_directory_uri() . '/js/main.min.js', array('jquery'), '4.0', true );

“jQuery is still in the <head>”

By default, jQuery is enqueued in the <head>. We can change this pretty easily in functions.php:

wp_deregister_script('jquery');
wp_register_script('jquery', "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js", false, null, true);
wp_enqueue_script('jquery');

In this example, I also chose for the Google CDN version of jQuery. Notice again the last param set to true, just like our enqueues above.

“jQuery is STILL in the <head>!”

Now you should see jQuery at the bottom of the DOM, right? Maybe not actually.

If you have a plugin that is dependent on jQuery and puts their script in the <head>, WordPress will keep jQuery in the head (as it should, otherwise, you’d get a “$ is not defined” error). You have a few options:

  1. Edit that plugin to make sure that nothing breaks if you set $in_footer to true in all of the enqueues. Assuming it works, contact the plugin author so they can change it.
  2. Use a plugin that moves all scripts to the footer (hit or miss).
  3. Deregister then reregister the plugin’s scripts. This is a little more risky because if the plugin changes the name of their script, you could get some broken JavaScript.

I’m partial to option 1, as it nips the problem in the bud, assuming the plugin author is responsive on the support forum. 🙂

At this point, you may run into a brick wall. Sometimes plugins do crazy things and it’s almost impossible to get the script/style where you want it. Luckily, the more well-known plugins (i.e. Contact Form 7) do these things correctly.

Get CSS to bottom of DOM

Getting CSS in the footer in WordPress is a little more tricky. The wp_enqueue_style() function doesn’t support $in_footer (maybe they’ll add it someday), so we’ll have to get our hands dirty. Instead of using the wp_enqueue_style() function, we are going to manually echo the <link> tag in the functions.php (I know, I know. Just after I lectured you on how great enqueuing is):

function styles_in_wp_foot() {
	echo '<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:400,700,400italic,700italic|Bitter" type="text/css" media="all" />';
	echo '<link rel="stylesheet" href="' . get_stylesheet_uri() .'" type="text/css" media="all" />';
}

add_action( 'wp_footer', 'styles_in_wp_foot' );

If you reload your site, you’ll see a flash of unstyled html until the css is loaded. We don’t want this. Enter criticalCSS.

criticalCSS

Now, we have to generate the inlined styles for the <head>. You can use a task runner like Grunt of Gulp to do this. Below is a Grunt configuration block. Please see the grunt-criticalcss docs for full implementation.

criticalcss: {
  custom: {
    options: {
      url: "http://localurl.vvv",
      // arbitrary width and height for critical to use when looking at "above the fold" styles.
      width: 1000,
      height: 800,
      outputfile: "inc/critical.css.php",
      filename: "style.css",
      buffer: 800*1024
    }
  }
},
Alternative to a task runner

Update: For those out there that are using a pre-built theme and don’t have a task runner, it might be easier to manually run a critcalCSS generator. Take the resulting css the tool gives you and dump it into the inc/critical.css.php file. Every time you update your css, you’ll want to run this tool again.

Now that we have that CSS in a file, we just need to echo the php include into the <head>. Add this to functions.php:

function criticalCSS_wp_head() {
	echo '<style>';
	include get_stylesheet_directory() . '/inc/critical.css.php';
	echo '</style>';
}

add_action( 'wp_head', 'criticalCSS_wp_head' );

Wrapping up

Screenshot of PageSpeed score of 96/100 for SuperiorCampers.comAs you see, the more scripts, styles and plugins you have installed, the more complex this can get. So keep those to a minimum, folks. 🙂

After this, you should see a huge uptick in your PageSpeed score if you are doing all the other (easier) things on the list. One of our clients’ sites has a 96/100.

Categories
Scripts Troubleshooting

SVG Wonkiness in Safari 5.1

I had a client who was still rocking an old school iMac that was running OSX Snow Leopard. That’s the latest version you can install, and as a result, can only upgrade to Safari 5.1. As I was using SVG in <img> tags on this site, they were rendering really oddly.

<img src="http://superiorcampers.com/wp-content/themes/supcamp_40/img/logo.svg" onerror="this.onerror=null; this.src='http://superiorcampers.com/wp-content/themes/supcamp_40/img/logo.png'" alt="Superior Campers">

It was as if the browser was adding padding: 50px 0; to the <img> but I couldn’t override it. I found a quick and dirty jquery fix for this. This works assuming you have an PNG fallback for your SVG’s, which you should for IE8 (at least as of the time of this writing).

//safari 5.1 polyfill hack
$(window).load(function() {
  //all images with .svg
  $('img[src*=".svg"]').each(function () {

    var
      $self = $(this);

    //If rendering really tall
    if ( $self.height() > 900 ) {
      //go to IE png fallback
      $self.attr('src', $self.attr('src').replace(/.svg/, '.png'));
    }
  });
});

Because Modernizr showed this browser as supporting SVG, the hack is to look at the .height() of the <img> after all resources are loaded, and if it’s over 900px, fallback to the PNG. Not pretty, but it fixed the issue. Let me know if you found a better fix.

Categories
Scripts

iOSifyMaps

In the iOS world, when you click on a Google Maps link, it opens in the native maps app. Let’s just do a quick hypothical. What if those maps links decided not to work anymore. I think the “maps:” href format is a little more future proof. I’ve written some JavaScript (note not jQuery) that changes these links to “maps:” formatting if and only if you are on an iOS device.

iOSifyMaps on GitHub

 

Categories
Scripts Tutorials

CSS3 Drop Down Menus

Surprisingly enough, I haven’t seen that many drop-down menus online that have the following:

  • Still work without JavaScript (css-based)
  • tabbing through menus functional

So I thought I’d put my money where my mouth is. Here is the Ryan Tvenge rendition of Dropdown Menus.

The html

<ul class="nav fade">
	<li><a href="#">Regular Lists w/ 2 child ul's</a>
		<ul>
			<li><a href="#">Child Item 1</a></li>
			<li><a href="#">Child Item 2</a>
				<ul>
					<li><a href="#">Grandchild Item 1</a></li>
					<li><a href="#">Grandchild Item 2</a>
						<ul>
							<li><a href="#">Great-Grandchild Item 1</a></li>
							<li><a href="#">Great-Grandchild Item 2</a></li>
							<li><a href="#">Great-Grandchild Item 3</a></li>
						</ul>
					</li>
					<li><a href="#">Grandchild Item 3</a></li>
				</ul>
			</li>
			<li><a href="#">Child Item 3</a></li>
			<li><a href="#">Child Item 4</a></li>
			<li><a href="#">Child Item 5</a></li>
		</ul>
	</li>
	<li><a href="#">Div wrapped menu</a>
		<div>
			You can put any html in here you want
			<ul>
				<li><a href="#">Even Lists</a></li>
				<li><a href="#">Even Lists</a></li>
				<li><a href="#">Even Lists</a></li>
			</ul>
		</div>
	</li>
	<li><a href="#">Item 3</a></li>
	<li><a href="#">Item 4</a></li>
	<li class="right"><a href="#">Right-Aligned Menu</a>
		<ul>
			<li><a href="#">Child Item 1</a></li>
			<li><a href="#">Child Item 2</a>
				<ul>
					<li><a href="#">Grandchild Item 1</a></li>
					<li><a href="#">Grandchild Item 2</a>
						<ul>
							<li><a href="#">Great-Grandchild Item 1</a></li>
							<li><a href="#">Great-Grandchild Item 2</a></li>
							<li><a href="#">Great-Grandchild Item 3</a></li>
						</ul>
					</li>
					<li><a href="#">Grandchild Item 3</a></li>
				</ul>
			</li>
			<li><a href="#">Child Item 3</a></li>
			<li><a href="#">Child Item 4</a></li>
			<li><a href="#">Child Item 5</a></li>
		</ul>
	</li>
</ul>
<h2>Vertical Menu</h2>
<ul class="nav vertical fade">
	<li><a href="#">Regular Lists w/ 2 child ul's</a>
		<ul>
			<li><a href="#">Child Item 1</a></li>
			<li><a href="#">Child Item 2</a>
				<ul>
					<li><a href="#">Grandchild Item 1</a></li>
					<li><a href="#">Grandchild Item 2</a>
						<ul>
							<li><a href="#">Great-Grandchild Item 1</a></li>
							<li><a href="#">Great-Grandchild Item 2</a></li>
							<li><a href="#">Great-Grandchild Item 3</a></li>
						</ul>
					</li>
					<li><a href="#">Grandchild Item 3</a></li>
				</ul>
			</li>
			<li><a href="#">Child Item 3</a></li>
			<li><a href="#">Child Item 4</a></li>
			<li><a href="#">Child Item 5</a></li>
		</ul>
	</li>
	<li><a href="#">Div wrapped menu</a>
		<div>

			Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

		</div>
	</li>
	<li><a href="#">Item 3</a></li>
	<li><a href="#">Item 4</a></li>
	<li><a href="#">Item 5</a></li>
</ul>

The CSS

/* CSS Dropdowns */
.nav { clear: both; list-style: none; padding: 0; margin: 0; float: left; width: 100%; }
.nav.vertical { height: auto; }
.nav li { display: inline-block; float: left; position: relative; }
.nav li:hover, .nav li.hover { z-index: 1; }
.nav li li { display: block; float: none; }
.nav li a { display: block; }
.nav.vertical li { float: none; display: block; }
.nav ul, .nav div, .nav li:hover ul ul, .nav li.hover ul ul { position: absolute; left: -9999px; top: 0; opacity: 0; }
.nav.fade ul, .nav.fade div, .nav.fade li:hover ul ul, .nav.fade li.hover ul ul { transition: opacity .2s linear; -webkit-transition: opacity .2s linear; -moz-transition: opacity .2s linear; -o-transition: opacity .2s linear; }
.nav div ul { position: static; left: 0; opacity: 1; padding: 0; background: none; }
.nav li:hover ul, .nav ul li:hover ul, .nav li:hover div,
.nav li.hover ul, .nav ul li.hover ul, .nav li.hover div { left: 0; top: auto; height: auto; opacity: 1; }
.nav li:hover.right ul, .nav li:hover.right div,
.nav li.hover.right ul, .nav li.hover.right div { left: auto; right: 0; }
.nav li:hover.right li:hover ul, .nav li.hover.right li.hover ul { right: 250px; } 

.nav li.hover ul li.hover ul ul, .nav li:hover ul li:hover ul ul, .nav.vertical li:hover ul ul ul, .nav.vertical li.hover ul ul ul, .nav div ul { position: static; left: 0;
	/* box-shadow */-moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none;
}
/* Customizable Areas */
.nav { padding: 10px; margin-bottom: 20px; /* css3 */ -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px;
	/* Gradients */
	background: #ffffff; /* Old browsers */
	background: -moz-linear-gradient(top, #ffffff 0%, #d6d6d6 100%); /* FF3.6+ */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#d6d6d6)); /* Chrome,Safari4+ */
	background: -webkit-linear-gradient(top, #ffffff 0%,#d6d6d6 100%); /* Chrome10+,Safari5.1+ */
	background: -o-linear-gradient(top, #ffffff 0%,#d6d6d6 100%); /* Opera11.10+ */
	background: -ms-linear-gradient(top, #ffffff 0%,#d6d6d6 100%); /* IE10+ */
	background: linear-gradient(top, #ffffff 0%,#d6d6d6 100%); /* W3C */
}
.nav div, .nav ul { width: 250px; background: #fff; color: #000; background: rgba(255,255,255,.9); padding: 10px;
	/* border-radius */ -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px;	
	/* box-shadow */-moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3); -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3); box-shadow: 0px 1px 4px rgba(0,0,0,.3);
}
.nav ul ul ul { background: none; padding: 0; }
.nav div { padding: 10px; }
.nav li a { padding: 10px;
	/* css3 */ -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px;
}
.nav li a:hover, .nav li a:focus, .nav li a.highlight { color: #005488; padding: 9px; border: 1px solid #f4f287;
	/* gradients */
	background: #fffcc1; /* Old browsers */
	background: -moz-linear-gradient(top, #fffcc1 22%, #fffd8c 100%); /* FF3.6+ */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(22%,#fffcc1), color-stop(100%,#fffd8c)); /* Chrome,Safari4+ */
	background: -webkit-linear-gradient(top, #fffcc1 22%,#fffd8c 100%); /* Chrome10+,Safari5.1+ */
	background: -o-linear-gradient(top, #fffcc1 22%,#fffd8c 100%); /* Opera11.10+ */
	background: -ms-linear-gradient(top, #fffcc1 22%,#fffd8c 100%); /* IE10+ */
	background: linear-gradient(top, #fffcc1 22%,#fffd8c 100%); /* W3C */
}
.nav li div a, .nav li ul a { } 
.nav.vertical { width: 200px; }
.nav li ul li:hover ul, .nav li ul li.hover ul, .nav.vertical ul li:hover ul, .nav.vertical ul li.hover ul { left: 250px; top: 0; }
.nav.vertical li:hover ul, .nav.vertical li.hover ul, .nav.vertical li:hover div, .nav.vertical li.hover div  { left: 200px; top: 0; }

CSS3 Transition Fading Option

If you add a class of “fade” to the parent <ul>, this will add CSS3 faded transitions to the menus.

Right-Aligned Menus

If you want a menu to go be aligned to the right, add a class of “right” to that menu’s parent <li>.

JS Helpers

There is also an accompanying JS file that helps out with a few things:

  1. Menus show up when tabbing. If you tab through the site, the appropriate menus will pop up. This does not happen out of the box with :hover pseudo classes and css.
  2. The menus work in IE6. This was something I was struggling with for awhile. I decided to put this feature in, just because it was so easy to do. I usually try to stay away from IE6 fixes. What the script is actually doing is adding a “hover” class to any anchor that is hovered over. This code also helps out with the tabbing feature.
  3. Highlighting parent anchor. When you’re mouse is hovered over a menu, the parent anchor of that menu will add the “highlight” class to that anchor, which essentially gives it the hover styling.

Vertical Option

What would a drop-down menu be without a vertical option. If you add a “vertical” class to the parent <ul>, the menu will format vertically and the menus will fly out to the right rather than down.

Outstanding Bug

The one bug that really drives me nuts right now is the fading out of the CSS3 transitions. On the vertical and right-aligned menus, it is fading to a different postion, then off of the screen. If anyone can find a fix for this, I would love to hear it.

The Code

demo // download