The 2017 ThinkShout Front-End Stack

Front-end development is full of challenges – changing design trends, browser idiosyncrasies, client demands, and ever-evolving web standards to name a few. Over the last few years though, a new challenge has emerged. Which development stack should you choose?

Once upon a time, front end development didn’t really have a “dev stack.” You had markup in files, maybe output from a CMS, a stylesheet, and some jQuery if you felt like getting fancy. Now though, the array of options can be paralysing. Pre-processors, post-processors, task runners, and package managers have made many aspects of development faster, but which ones are best? Where do you start?

Here at ThinkShout, under the watchful eye of Eric Paxton, our Senior Front End Engineer, we’ve been trying out the various options whenever we take on a new project, to see how well it fits in our theming process. We’re pretty busy, so this covers a lot of ground quickly. We’ve been careful to fully document the tools used in the past so that we don’t bedevil the maintenance folks. (We are often the maintenance folks).

The last few builds have seen our dev stack settle down to a flexible tool set that is easy to setup and maintain, while providing us with excellent modern theming tools. Let’s dive in!

Getting Started: Languages, Handlers, and Package Management

At the bottom of a development stack are the languages used, the language handlers, and the package managers that allow you to include pre-built tools and libraries in your project. Some of these are interchangeable, but it solves a lot of problems if everyone uses the same fundamental tools.

In our case, we use Ruby and JavaScript as the base languages, and rbenv and Node as their handlers. By using Ruby and JavaScript, we get access to an extremely wide array of applications, tools, plugins, and more. Once these are installed (Using an OS package manager! In this case, Homebrew (since we all use Macs), we add package handling for these languages: Bundler and NPM respectively. This gives us the following base:

  • Ruby via rbenv, managing gems using Bundler
  • JavaScript via Node.js, managing packages using NPM

Now we can specify Ruby Gems and Node packages in a Ruby Make file (Rakefile), and a complex project setup is as simple as running rake install once from the theme directory, and starting the task watcher using rake serve. (To be more precise, we use the Rakefile to install the Ruby Gems as defined in the Gemfile, and the Node modules as specified in the package.json file).

The complete project setup for a new developer would be the following:

~: brew install rbenv
~: gem install bundler
~: brew install node
~: brew install npm;
~: cd ~/path/to/theme/directory
~: rake install
~: rake serve

After that, any new projects would only need the last three lines run.

The key to making this work is to have a Rakefile, a Gemfile and a package.json set up in our project’s theme so that rake install works properly. In our case we use the Rakefile to first run bundle install, which installs the appropriate gems and their dependencies:


task :install do
     system 'bundle install' # this runs the Gemfile contents!
     system 'npm install -g browser-sync'


source ''
gem 'sass'
gem 'sass-globbing'

This generates a Gemfile.lock listing all of the installed packages/versions.

The npm install lines in the Rakefile setup tools that we’ll discuss later. Our next layer in the stack are the SASS tools that Bundler installed.

SASS at ThinkShout (please pass the Bourbon)

In the middle of our stack is [SASS]( We use SASS in a fairly simple way at ThinkShout, installing it with [sass-globbing]( This allows us to set up directories that allow any files using the appropriate _filename.scsssyntax to be included in the build. We also tend to keep the directory structure fairly minimal:


@import 'lib/bourbon/bourbon';
@import 'lib/neat/neat';
@import 'lib/normalize/normalize';
@import 'global/*';
@import 'layout/*';
@import 'modules/*';

The first thing we include is the Bourbon mixin library. This includes coding shortcuts such as the pixels-to-rems syntax rem(24). This allows us to read a design’s pixel spacing and it converts them to the appropriate rem values. The Bourbon Docs are excellent and well-maintained as well. Never worry about browser prefixes or fallbacks again.

Next up is the Bourbon-related grid framework, [Neat]( A simple but powerful grid that uses semantic markup and easy-to-read terminology such as @include span-columns(9). No extra wrappers, no specific classes to add, and it’s extremely robust. We haven’t run into any cross-browser issues in over two years of using it, which says a lot, and since it’s only applied as you specify, it’s easy to break out of the grid if you need to.

Next up is [normalize.css](, a modern update to the old CSS reset stylesheets. Not really much to add to that except it’s really well commented, so make sure you change it from normalize.css to _normalize.scss so that you don’t bloat your final site.css file.

The Global directory has the following:


The _01, _02, etc. prefixes take advantage of the sass-globbing’s alphabetical file inclusion. All our site variables (colors, font weights, and so forth) are in vars, our custom mixins are next, then extends. Base has all of the base markup styles:

body {
  font-size: rem(16);
  font-style: normal;
  font-weight: $regular;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

h1, h2, h3, h4, h5, h6 {
  text-rendering: optimizeLegibility; // Fix the character spacing for headings

p, a, ul, ol, etc...

The layouts directory usually has a _layouts.scss file, which covers the basics of site layout. Since we use Drupal, we’ll often add a _regions.scss as well to specify layout for the various Drupal-generated content zones on a site. These files are where we use the Neat framework the most.

Finally, we have the modules directory – where most of the theming takes place. These are usually named by content type (_basic-pages.scss, _articles.scss, etc.), though there are often files such as _forms.scss and _homepage.scss as well. Sometimes we don’t even have to use our source maps to know where code is!

One of our good habits is to start with our mobile-first, responsive _01.template.scss file:

// Default / Mobile

// Tablet (580px)
@media all and (min-width: $tablet) {

// Large Tablet (768px)
@media all and (min-width: $lg-tablet) {

// Desktop (1228px) $max-width: 1440px
@media all and (min-width: $desktop) {

When you want to add another theming module, you just make a copy of the template and your progressive breakpoints are included! (The $max-width: 1440px is there in a comment because it’s handy).

All of this gets handled by a task in our Rakefile, which sets a watcher for changes to any SASS file and compiles them into a single css/style.css:

desc 'Watch sass'
task :sasswatch do
system 'sass -r sass-globbing --watch sass/style.scss:css/style.css'

Pulling It All Together: Browsersync!

Finally, at the top of our stack, we have Browsersync. Eric Paxton, our Senior Front End Engineer, wrote an excellent overview of why we use this amazing tool, what it does, as well as how to install it in detail for Drupal 8.

In our stack it’s as simple as another task in that Rakefile:

desc 'Running Browsersync'
task :browsersync do
     system 'browser-sync start --proxy "" --files "css/*.css" --no-inject-changes'

And adding the following (generated by running browser-sync start) to the site’s <head> :

<!-- <script id="__bs_script__">
  //<![CDATA[ document.write("<script async src='http://HOST:3000/browser-sync/browser-sync-client.2.12.3.js'><\/script>".replace("HOST", location.hostname));
</script> -->

This also sets a watcher on the CSS, and refreshes every browser you have open to localhost:3000 or the local network IP address it generates upon running rake serve.

The last part of the Rakefile implements the tasks we set up:

desc 'Serve'
task :serve do
  threads = []
  %w{sasswatch browsersync}.each do |task|
    threads << do |devtask|
  threads.each {|thread| thread.join}
  puts threads

This has the magical effect of opening a new browser window to localhost:3000 when you run rake serve, and reloading it every time you save any of your SASS files. It also scrolls all open windows together, even when you open up things on your phone using the local network proxy, which it helpfully provides as output:

>>> Sass is watching for changes. Press Ctrl-C to stop.
[BS] Proxying:
[BS] Access URLs:
       Local: http://localhost:3000
          UI: http://localhost:3001
 UI External:
[BS] Watching files...
[BS] File changed: css/style.css
      write css/style.css
      write css/

This is really the cherry on top of the dev stack – after using it for a little while, you’ll wonder how you ever got along reloading everything manually.

Stack Overview

In summary, here’s that front-end stack:

  • Ruby via rbenv, managing gems using Bundler
  • JavaScript via Node.js, managing packages using NPM
  • SASS with globbing, set up in a simple directory structure
  • Bourbon Mixin library
  • Neat Grid system
  • Normalize.css as _normalize.scss
  • A simple module template containing responsive breakpoints
  • Browsersync

None of this is carved in stone of course, and it gets slightly tweaked for every new build based on the project requirements, such as internationalization, the base CMS (Drupal, WordPress, or Jekyl in our case), and the desire to try out something new, which is fairly constant. After all, that’s how we got to the stack we have today!

Up and Running with Drupal 8

(Originally posted on

It’s November 2015, and is rolling out Drupal 8 release candidates! Heck, Drupal 8 is coming out this week. For many devs, that means we need to figure out how to install, sync, and set up a site theme. Here at ThinkShout, learning is part of the job – we have dedicated time every week to learn new things and share them, be it in a blog post, at our weekly engineering meetings, or at our weekly team lunch.

For the last few months, we’ve made learning Drupal 8 our singular focus. The ‘D8 Bookclub’ has been getting assignments, doing their homework, and sharing their findings internally. We’ve now spent enough time with Drupal 8 that we can share what we’ve learned, the little gotchas and hard-to-find code snippets, as well as the general change in philosophy that has occurred in the shift from D7 to D8.

We generally agree that one of the things keeping people from digging into Drupal 8 is the difficulty of getting it installed and running in a deployable way, which is a must-have if you want to have a team working on a project. This article will walk you through Drupal 8 installation and configuration sync. The follow-up article (Up and Theming with Drupal 8) will cover theme setup and configuration, so that you can finally get going with theming in D8.

Installing Drupal 8

The composer install method is being widely proposed as the replacement for drush make. The composer.json file is to composer install what a drush make file is to drush make. The drupal-composer github repo has a composer template called drupal-project that will get us up and running with just the basics necessary to have a working site.

For command-line manipulation, you’ll need to be using Drush 8. Like most dev shops, we use version control (Git) as well as a local->dev->test->live server setup. There are a few gotchas here:

The composer template mentioned above works great for a basic D8 install, but if you want it customized (additional packages, post-install commands, etc), you will have to:

  • download the full composer profile
  • unzip the files
  • update the composer.json and scripts/composer/ files
  • run composer install locally.

This is the method we’ll be using for the rest of this post.

First, install composer. We’re using OSX with Homebrew, so this is fairly simple – brew install composer. Note: after implementing this, add this to your .bashrc (or similar):

export PATH="$HOME/.composer/vendor/bin:$PATH"

Next, ensure you have Drush 8 installed. We prefer the Composer global install approach to make updating Drush as simple as running composer global update.

Next, download the full composer profile, and unzip it into an appropriate local directory (We use a ~/Sites/ directory to hold all of our projects).

Note: the Drupal Composer project updates regularly. If you run into any errors, re-download it. You could alternately create an installation profile in the web/profiles folder, but that seems to be a tad under-documented and still not fully baked.

Now that we have a default site scaffold in place, we can get back to the post-install method. If you want to run the site installer after composer installs Drupal, in the file, you would add:

cd web;drush si --site-name="SITENAME" --db-url=mysql://root:PASSWORD@HOSTNAME/DBNAME -y;cd ../

For the above, replace SITENAME, PASSWORD, HOSTNAME(we use localhost) and DBNAME.

One of the D8 Bookclub challenges was completing the installation without any warnings appearing on the Status Reports page at /admin/reports/status. We also want to specify the configuration sync directory, so that it’s not site-unique.

The trick here is to use a series of permission and site config tweaks. After the site install code above, add the following to your

chmod 777 web/sites/default/s*;

#Prepare the custom sync directory, which will sit outside of the web root
if [ ! -d configs ] then mkdir -m777 configs fi

echo "\$config_directories['sync'] = '../configs';" >> web/sites/default/settings.php

echo "\$settings['trusted_host_patterns'] = array('SITENAME\.dev$',);" >> web/sites/default/settings.php;

chmod 444 web/sites/default/s*;

chmod -R 777 web/sites/default/files;

This will make the settings.php file editable, create a sync directory below the web root (bonus security!), add the sync directory path, add the trusted host pattern (a new D8 requirement), secure the file, and make the web/sites/default/files folder globally writable.

Now you’re ready to run the installation! From the base directory, run:

composer install

This will take a while, since it’s downloading Drupal and all the modules. After initial installation, perform a full export and an immediate import/sync of your site configuration profile. This can be done two ways:

  1. Via the gui: /admin/config/development/configuration/full/exportSave this export file! Any subsequent sites will need this as a starting point so that entity mismatches don’t occur.
  2. Via Drush (run from the /web directory):
drush config-export
drush config-import sync

This will export and then sync all of your config files in the configuration directory we specified in the post-install script.

You would then check in this version of the site – a commit message such as ‘Base Site profile’ would be helpful. Here’s a quick set of command-line git repo creation commands, starting with an installation of hub, the command-line wrapper for GitHub (this will allow you to use GitHub’s 2-factor authentication):

brew install hub
git init 
hub create
git add .
git commit -m 'Base Site profile'
git push --set-upstream origin master

After that, you can check out additional sites by creating a directory, cd into that directory, and running a git clone command inside it, such as:

git clone .

Now you can run composer install to build your clone.

Optional: edit the prior to running composer install if you’d like your clone to have a different hostname, db, etc.

Syncing Sites

Site configurations are only exportable to sites that have the same UUID and have synced using the shared base configuration profile. To find your site’s UUID, cd to the web folder of the base site, then run drush config-get After that, any new site can be synced using these steps immediately after a fresh install (either via download or Git clone):

drush config-edit (Update the UUID to match the base site)

If you manually saved the files, upload the base site config profile, but do not sync it
If you cloned the repo, the files should be in place. If the git repo has moved beyond the base install, check out the repo at the ‘Base Site Profile’ stage mentioned above using git reset --hard $SHA1 (where $SHA1 is the SHA of the Base Site Profile)

drush config-import --partial

Important Note: Every new instance MUST start with the same base configuration profile, otherwise you may have entity mismatch issues, even with the partial import. After the initial sync is complete, you can pull in config files via Git and sync will work as expected, even over multiple configuration changes.

Creating a Theme

This will be covered in our next post: “Up and Theming with Drupal 8.”


The post was written at the end of 2015 while Drupal 8 was still in a Release Candidate stage. While some effort will be made to keep the post up-to-date, if it’s after 2016, you should probably be adding the year you are currently in to your Google search, or better yet, check the docs on


When I was very young, I had a dear friend whose name was Frank.

He was a year or two older than me, but we were like brothers. We even did a blood brother ceremony during a sleepover when we were… 7? 8? It’s hard to remember.

Which is to say, we were as close as two friends can be at that age. He was the first person outside my family that I ever loved.

We were friends for about 4 years, until my parent moved to get away from the oppressive nature of suburban law and watchful neighbors. But for a year or so, Frank and I were in the same school. It was Tonquish elementary school on Warren road (now a church). We lived on a street called Blackfoot, in Westland, Michigan. All the houses were the same;  late 1960’s – early 1970’s ranch houses.

School was unremarkable, except for one odd thing. There was a girl who was in and out of school, whose name was… Beth? I don’t recall much about her except that one time when she was in school, someone pulled her hair, and it turned out it was a wig (I think they knew, which is why they pulled on it). She lived just four houses down from me, but I never saw her playing, so we were not friends.

When they pulled off her wig she was nearly bald, and I remember everyone laughing as they moved away from her. For my part, I recall being confused. In my memory, she clutched her books to her chest and looked down, humiliated, as a teacher waded in and broke up the spectacle.

Some time after that, perhaps after school was out for the year, my friend Frank came to me with a grim look on his face.

“You know that girl Beth, who we went to school with?”

I said yes, I remembered her.

“You remember? We laughed when someone pulled off her wig?”

Yes, I said. I remembered that.

“Well,” he said softly, looking down. “Well… my mom said she had… Lu-ke-mia?  And she was sick, which is why she lost her hair. But after school was out she went back to the hospital. And… she died.”

Frank looked at me with a burning stare. “We laughed at her.”

“Yeah.” I said. It was true.  We all laughed at the girl who lost her hair. I felt so ashamed.

Frank stood for a moment full of rage. “We should go tell her mom we’re sorry.”

“Yeah.” I said. “We should. I’ll go with you.” I recall being very scared.

So we walked down the street to the house where we knew she had lived, and Frank rang the bell. A woman answered the door, who looked to be about 30, but also looked like she had not slept in many years. Dark hair, no makeup, and the saddest eyes I have ever seen on a living person.

“Yes?” she said, slightly confused, as she held the screen door open a little.

Frank spoke haltingly – “Ma’am, uh… We just wanted to tell you that we teased your daughter when we were in school, but we didn’t know she was sick, and we’re really sorry.”

The woman burst from the door with a cry and wrapped her arms around Frank. I jumped back in an act of pure self-preservation, presuming that the end was near for both of us.

The woman, Beth’s mother, wept uncontrollably on Frank’s neck. “Oh my darlings,” she sobbed, “It’s OK. You didn’t know. I forgive you. Oh my god, thank you. Thank you, thank you, thank you. It’s all right, you didn’t know. I love you. Thank you for telling me this. Thank you. I forgive you.”

She broke down so hard. I don’t remember a lot of it because it was so emotional. I know we were all crying. She went back into her house eventually, and closed the door.

Frank and I walked out to the sidewalk, shaken, and very quiet.

One of us said, “I’m glad we did that.”  The other nodded. “Yeah.”

Frank looked angry again, and looked at me.

“Let’s make sure,” as he teared up, “that we never, ever make fun of someone like that again. Never Ever.

I was feeling Franks anger myself, just thinking about it.

“Yeah.” I said, “We should never do that to someone.”

“Ok, then. We swear, as blood brothers…” as he held out his hand.

I took it, and said with all the heart a 10 year old human has, “I swear. Never. Never ever.”

~ Joe K

Smoked Pepper Vodka

    Use this file Immature jalapeno capsicum annuum var annuum
CC BY-SA 3.0 –

Wash 6 plump, firm, smooth-skinned Jalapenos or other similar peppers, then cut them in half lengthwise. Remove ribs and seeds. You might want to wear rubber gloves for this!

For a gas stove, use metal tongs and cook the Jalapenos skin-side down over a medium flame until well blackened. For an electric stove, set a wire cooling rack over a burner on high and set them skin-side down until well charred.  If your stove hood vents outside, set it to high, otherwise open your windows. A grill also works well.

Immediately after blackening, put them in a covered dish until cool. Rinse under cool water & rub the black skins off.

Slice thinly, put them in a quart mason jar, and cover w. vodka. Age in the fridge for about two weeks, shaking now and then.

After two weeks, decant the Vodka into decorative bottles w. a few pieces of Jalapeno for decoration.

I have generally used Tito’s vodka, but anything in the $15-25 range should be OK.

Welcome to Portland

Crossing the Buckman Bridge
Crossing the Buckman Bridge. It’s windy.

The past few  weeks have been a bit of a blur. My good lady wife Rachel and I decided last year that we should move to Portland (PDX) from Raleigh-Durham (RDU) for several excellent reasons, to wit:

  • The weather in RDU is overbearingly hot and sweaty in the Summer, a period that spans Apr-Oct, roughly.
  • I’ve suffered constant flu-like allergies every year I’ve lived in the state, mostly due to ragweed pollen.
  • We’d like to downsize our lives as much as possible, but that’s not really an option in a city where two cars are more or less mandatory due to sprawl and poor public transportation.
  • My wife would like to start her business in a location that has a good blend of tech and a strong independent art/comics scene.

Portland really seems to fill all of our requirements, and then some – the cost of living in a high-density urban location is about the same as RDU suburbs, with lower costs for food and transportation.

Continue reading Welcome to Portland

2013 in Review

All in all, an excellent year.

  • Did the Run Free marathon in Feb with excellent friends.
  • Finished the house renovations.
  • Really dug in at work and loved it. Did the font-end development for several cool websites, and blogged about it.
  • Went to Berlin with my Good Lady Wife and had a lovely time.
  • Successfully avoided most relatives and their attached drama for the entire year.
  • Ran regularly, getting up to 12 miles in Summer.
  • Managed to keep my weight under control (+/- 5lb).
  • Learned to cook Indian food.
  • Reduced my personal possessions down to what will fit in a storage/moving pod.
  • Spent the week of Christmas in Philly with my lovely wife and some very cool friends.

Best of all, I had my fantastic and brilliant wife (and best friend) Rachel to share it with. 🙂

I’m looking forward to many exciting challenges in the New Year, since we’re starting 2014 by selling our house and moving to Portland, OR.

I think I shall write more in the New Year, and perhaps do more cartooning.

See you in Portland!

~Joe K

Enhance Your Profile

A little over a year ago, I switched from PC-based front end development, to Apple/Mac based development. I had been doing Mac and PC desktop support in my previous job, so I was used to OSX, the Mac OS. However, the switch also required me to upgrade the tools I use on a daily basis, including my source code editor and web server platform. It also (finally!) got me working with the Git version-control software on a daily basis, something I had been looking forward to (all the cool kids use Git. Also, no more lost code.).

The best thing (to me) about using Git is that it has me using a Bash shell again, which means I can tweak out my .profile. What’s that you might ask? Why, let me show you!

Continue reading Enhance Your Profile

Wild in Kentucky

Web development is a great field to work in for a lot of reasons, but for me the best reason is the variety. One day you’ll be adding a guest editor to a high-volume cooking website, the next day you’re adjusting the responsive menu on a drug-culture magazine, and the day after that you’ll be tweaking the careers page on a material handling solutions company’s site — it never gets boring.

Continue reading Wild in Kentucky

Front-end Developer Employer Questions

The Front-end Job Interview Questions list ( is pretty awesome–if you’re an employer. It gave me the idea to have a list of questions for front-end developers to ask prospective employers.

These questions cover employers who are large enough to have a dedicated development team. It should work reasonably well for both agencies and companies that are working on internal projects.

Interviews usually happen with both Managers and Team Members, so I’ve split the questions into those two general categories.

Continue reading Front-end Developer Employer Questions

Code, Essays, and other thoughts by Joe K~