ponyfoo.com

Modularizing Your Front-End

Fix
A relevant ad will be displayed here soon. These ads help pay for my hosting.
Please consider disabling your ad blocker on Pony Foo. These ads help pay for my hosting.
You can support Pony Foo directly through Patreon or via PayPal.

In the past I’ve wrote about a small alternative to async, named contra, which is barely over 2kb, and has the browser at its heart. It comes with the usual suspects, allowing you to craft asynchronous flows in series or concurrently, as well as a few functional methods, an event emitter implementation, and a queue which powers most of the library.

contra.png
contra.png

I’ve also discussed campaign, where I modularized email sending and templating. That was really mostly a port of what was already in Pony Foo’s code base, and thus open-source, but written in a more portable way that could be used across other projects or efforts. In that occasion I explained the motives for developing the library as well, which mostly revolved around having a reusable email sender that was easier to bring into a Node application than the existing alternatives.

In this article I’ll describe the development process, choices I’ve made, and contents of my latest open-source projects. In lieu with Pony Foo’s humble, humble beginnings, I’ve decided to continue on the path set forth back when I wrote campaign, and re-do Pony Foo from scratch. The difference is that this time I’ve been putting more of a focus on modularity, basing off of what I’ve learned over the last year (and from the glaring mistakes I made implementing the original blog engine).

  • flexarea is a tiny library that helps humans resize <textarea> elements
  • hint is a pure CSS implementation of the tooltips you can see today on Pony Foo
  • taunus is a micro MVC framework I feel extremely excited about

Caution: This article contains words about Browserify, npm run, Gulp, framework-less JavaScript, and ponies. Reader discretion is advised. Also, not all of it might make sense.

If you’ve glanced over the projects, you’ll realize that both flexarea and hint are quite simple. However, they are also highly reusable, and it’d be interesting to share

Flexible <textarea> elements

Flexarea just provides a little grip you can drag to resize a <textarea>, and nothing else. This is how it currently looks like.

flexarea.png
flexarea.png

The API is fairly simple. flexarea is a function, it takes a DOM node, surrounds it with a wrapper element, and appends the “grip” element to the wrapper. The grip is what’s on the bottom of the <textarea> shown in the picture above. As you can see in the repository, I used a Common.JS module for the component, meaning that I need Browserify in a compile step to try the component out, as well as during releases. For local development I deeply recommend you to familiarize yourself with npm link if you’re a package author, as it’ll considerably speed up your development productivity.

Gulp, npm run, Whatever

There’s a very similar Gulpfile to the one I detailed in My First Gulp Adventure. The “big” difference in the Gulpfile is that I run Browserify, given I’m using Common.JS in my source code. Browserify takes a little dancing in Gulp. Here’s how it ends up looking like, after removing some of the cruft, getting us just the core idea.

var gulp = require('gulp');
var streamify = require('gulp-streamify');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var source = require('vinyl-source-stream');
var browserify = require('browserify');

gulp.task('build', function () {
  return browserify('./src/flexarea.js')
    .bundle({ debug: true, standalone: 'flexarea' })
    .pipe(source('flexarea.js'))
    .pipe(gulp.dest('./dist'))
    .pipe(streamify(rename('flexarea.min.js')))
    .pipe(streamify(uglify()))
    .pipe(gulp.dest('./dist'));
});

The code above would compile my source code into a UMD module (also read Writing Modular JavaScript by Addy Osmani while you’re at it). That is pretty awesome. You write CJS code, with all the benefits that that implies, such as no implicit globals and no IIFE hell, and you get a module that can be run and included pretty much everywhere you can run JavaScript! Except there’s no way in hell compiling a single file into two distributions should involve around 15 lines of code. Aren’t you using a library to simplify things? Compare that to using npm run and the browserify CLI.

browserify src/flexarea.js -s flexarea -o dist/flexarea.js -d
browserify src/flexarea.js -s flexarea -o dist/flexarea.min.js -t uglifyify

Anyways. Lately I’ve been leaning more and more into the “whatever” camp. I find that it’s much leaner to just use npm run, and putting together a build file is incredibly faster if you aren’t dealing with a formal build system such as Grunt, or Gulp. The pretty awesome part of npm run some people are surprised about is that you get access to node_modules for free. Meaning that rather than having to do something weird like node_modules/browserify/bin/browserify {args}, npm figures all of that out for you.

You Were Saying

Right. So. The flexarea thing. The code itself is irrelevant to this article, but I want to focus on two aspects of how I put it together.

“nico-style” CSS

I’ve come up with a CSS naming convention similar to BEM, but without all of the weird dashes (for the most part). Rather than splitting your CSS declarations in blocks, elements, and modifiers, I’ve come up with something arguably more pragmatic. You should start with a unique namespace for your component, preferably 2 to 4 characters long, such as fa in the case of flexarea. Then you can use as many words as you’d like to name classes associated with that component. You might decide to have fa-container, fa-items, fa-item, and fa-item-selected, or just fa-selected. In practice it won’t matter all that much as long as the class names are reasonable and you don’t use a single namespace for various components.

Indeed, sometimes you’ll find yourself in need of slightly changing a component in a particular view or maybe when it’s inside of another component. The preferred approach here is to duplicate the class name but keep the parent namespace. Suppose you have a fa component in the at (accounting) view, and you want to change the bottom margin for fa-item in the accounting view. Where possible, the second approach is preferred, but it’s also decent to have one or two levels of selector nesting.

.at-container .fa-item {
  margin-bottom: 5px;
}
.at-item {
  margin-bottom: 5px;
}

If you are interested in this pattern, you might find it useful to check out the Stylus files in ponyfoo@redo. Note how I also keep components one per file, and modularize the style rules as aggressively as possible. Files are cheap, but a 2000 line stylesheet is every front-end developer’s nightmare. Raise your hand if you’ve seen one of those written in LESS, SASS, or Stylus, containing rules over six nesting levels deep. I have. It’s not pretty.

Disclaimer: I didn’t dub it “nico-style”, but I didn’t bother to give the convention a better name either.

Framework-less Development

The other aspect of flexarea that I think is worth mentioning is that it’s framework-less. It has every reason to be. Why would you make a commitment to a large library like jQuery just to provide a little bit of functionality such as a resizer? Would you add jQuery UI to that? Where does it end? How much did you really gain by adding all those extra constraints? It must be a lot, because on the flip side, you are getting bigger, and more importantly, you are limiting your project to “nerds who use jQuery in their application”, where you could just be writing code for “any nerd with a browser”. If your code didn’t really depend on the DOM API, like in the case of contra, then you could be targeting just about anyone with a JavaScript interpreter.

Okay sure, so you need promises, and jQuery provides an implementation, oh – how convenient! Well, you certainly didn’t have to include all of jQuery for that. You could at least try and use a custom build. Or you could also find yourself a Promise library which only does promises (and is closer to the ES6 Promise spec), such as rsvp. That sounds great! At least, you won’t be turning down users who don’t have jQuery in their projects.

I believe jQuery is becoming less relevant, and that a polyfill-oriented approach should be preferred over using jQuery. This is not just insano-purist talk, jQuery damages mobile web performance. jQuery UI annihilates it. Before you go ahead and post a comment, I’m not just talking about the size of these libraries. jQuery takes a long time to be interpreted in mobile devices. This happens on every request, regardless of how aggressively jQuery gets cached. That’ll bite your page load times, making them slower every time.

A polyfill-oriented approach should be preferred over using jQuery

Polyfills. Indeed, my mention of rsvp was just auxiliary to introducing es6-promises, a Promise polyfill based on rsvp. The awesomeness in ponyfills is that they aren’t opinionated about the feature’s API. If you want to use a feature, and there’s a native implementation coming to most browsers, it makes sense using a polyfill because it’s a drop-in fix for browsers where it’s not yet implemented. You are also theoretically able to remove the polyfill cleanly once you deem it no longer necessary, based on the analysis of user agents coming your way. You just remove the reference and you are done. There’s no large commitment, which would result in anxious keyboard pounding and long afternoons tediously refactoring your way out of an opinionated library’s API.

time.png
time.png

TL;DR If there’s a polyfill that already does what you need, then always prefer that over a library that forces you to adopt their own API. Here’s a few you can use to bring browsers up to speed by enabling features like dataset, classList, and others.

Hint

Hint made an even happier Pony. Remember the little tooltips that you get when hovering something in Pony Foo? Come on. They’re everywhere!. Here’s another.

hint.png
hint.png

The hint in the figure shown above, though, isn’t in production but rather using the hint micro-library. I feel particularly good about hint because I’ve removed all JS from it, making it into a pure-CSS solution. All you have to do is add the stylesheet to your page, and then you can create hints with a very reasonable piece of markup. It’s quite semantic, and you don’t have to waste time telling a JavaScript library when it should be turning your attributes into the thing you want.

<a data-hint='The draft will be deleted'>Discard Draft</a>

The cute thing about hint is that I wrote it in Stylus, but I still provide a CSS distribution that gets rebuilt during releases. Certainly, it involves a bit of Gulp work, but it’s not as bad as with the Browserify example we saw earlier.

gulp.task('build', ['clean'], function () {
  return gulp.src('./src/hint.styl')
    .pipe(stylus())
    .pipe(gulp.dest('./dist'))
    .pipe(rename('hint.min.css'))
    .pipe(minifyCSS())
    .pipe(size())
    .pipe(gulp.dest('./dist'));
});

That being said, npm run stylus would definitely be leaner and clearer. I guess that copying and pasting did win this battle on behalf of Gulp, though.

stylus src/hint.styl -o dist/hint.css
stylus src/hint.styl -o dist/hint.min.css --compress
wc -c dist/hint.min.css

That’s as far as builds go, but what about the component? I’ve managed to turn the solution into a pure CSS one by getting rid of assumption that hint cared about whether or not the attribute was empty. It’d check that the hint wasn’t empty and provide an API to either enable or disable a hint tooltip. Oh, also, that code was written in jQuery. I decided that that validation was better left to the consumer, who can decide exactly when and how to apply data-hint attributes on their DOM elements, and got rid of the JavaScript code.

I mentioned Stylus, so I think it’s also worth mentioning how easy it is to include hint in your project provided that you are already using Stylus yourself. This is how I do it on ponyfoo@redo. There’s no extra build step involved, because Stylus takes care of dependency resolution, much like Browserify does.

@import 'node_modules/hint/src/hint'
That’s it!

I’ll leave Taunus for next week! I think it deserves its own article. (and a little more polish!)

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments