Boyleing Point

7/11 was an inside job

In Agander, I made my first forays into colour themes. In a very simple approach, I have two colour schemes (light and dark) which are displayed on the body as a class (scheme-light and scheme-dark) respectively. The general approach for styling a component is as such: _button.scss

// Define base component styles (e.g. sizing/positioning)
.button {
border: 1px solid;
padding: 6px 5px;
}
// Dark Color scheme styles
.scheme-dark {
.button {
background: white;
border-color: white;
color: black;
}
}
// Light Color scheme styles
.scheme-light {
.button {
background: black;
border-color: black;
color: white;
}
}

Although this is quite lightweight, there are still issues.

  1. It puts a hard dependency on codebase changes to add, remove or modify themes,
  2. It makes user defined colour schemes all but impossible
  3. Simple component partials are no longer neat self-contained partials with one selector defining all the component styles
  4. There are several cases where I need to have colours that contradict the global colour scheme (e.g. black text for the white modal dialog) and it requires the use of !important and many colour overrides.
  5. The extensibility of the approach is very limited because as more themes are added, the stylesheets WILL get bloated and overweight.

Enter the CSS Variable (the hero we need) CSS Variables are defined like so:

:root {
// Initialise the variable
--primary-color: pink
}
p {
color: var(--primary-color); // it's pink, baby.
}

The var function also takes a second argument which is an initial/fallback value.

p {
color: var(--primary-color, red);
}

CSS Variables follow block scoping principles, so, variables defined in :root are considered to be global variables (but may be overwritten inside specific components) and variables defined in any other element are scoped to that block of styles. This is broken down very nicely on a recent Smashing Magazine article.

How can CSS Vars help Agander?

I recently wrote a library to ingest variable names and values and spit them onto the root element (see the package) The idea is that each theme would have all relevant variables defined in objects like so:

const viewState = {
currentTheme: 'darkScheme'
}
const themes = {
darkSheme = {
'primary-color': {
hex: '#FFF'
}
},
lightScheme: {
'primary-color': {
hex: '#000'
}
}
}

And then when the currentTheme changes:

import syncVars from '@lukeboyle/sync-vars';
function updateCssVariablesWithCurrentScheme(colorScheme) {
syncVars(themes[colorScheme]);
}
// if we call that function with 'darkScheme'
updateCssVariablesWithCurrentScheme('darkScheme');
<html style="--primary-color: #FFF;"></html>

So, how does this help? For one thing, with this approach, I no longer have to worry about adding the colour scheme classes to the body, and I don't have to do any hacky overrides, etc. _buttons.scss now looks like this:

.button {
border: 1px solid var(--text-color-var);
padding: 6px 5px;
background: var(--button-background-color-var);
color: var(--text-color-var);
}

Looking forward, this approach also means that custom colour themes are very nearly in reach. It also means that colour schemes could be changed on the fly. The user could have a colour swatch tool and be previewing their theme changes live. Taking it even further, it means that the colour schemes no longer need to be a part of the codebase. It could just as easily be a JSON file on the server and changes could be flexibly pushed. Why is this exciting? Say it's Christmas time and you want to get into the spirit of things... With a few string replacements you have a temporary festive theme to force upon your users.

Other Applications

Accessibility

Sites or apps could have buttons to activate color blind mode and specific 'problem' colours could be swapped out for friendly colours. Additionally, high contrast modes would be a breeze.

Easter Eggs

Users could activate alternate modes for websites to get a different experience.

Retrospective

CSS variables are getting me really excited because it's the first minimal overhead approach to theming in front-end only applications. This is something that will reward well structured stylesheets and result in a better experience for the user. I am looking forward to rolling out custom themes in Agander and finally getting around to making the flat UI theme I have wanted to make for some time.

I was recently given the job of rebuilding a particularly bad landing page from an external company. Apart from class names, styles and markup being all over the place, there was a particularly obnoxious form validation script sitting in the middle of the page. An excerpt of the script can be seen below, and this documents the process I took when reviving the JS side of things.

<script type="text/javascript">
var flagValidation;
/* validation for 'phone number' */
function PhoneNumberValidation() {
var phoneNum = document.getElementsByName("Phone")[0].value;
var normalPhonepattern = /^[0-9s-+]{6,14}$/g;
if(!normalPhonepattern.test(phoneNum))
{
flagValidation = false;
document.getElementById("PhoneValidation").innerHTML = "Only numbers, '-' and '+' characters are accepted"
}
else
document.getElementById("PhoneValidation").innerHTML = ""
}
function SubmitDetails(){
flagValidation = true;
PhoneNumberValidation();
return flagValidation;
}
</script>

So what is wrong with this picture? - There's no reason for this to be a script tag on the page, let's make it an external script - Mutation - Basing the validation on mutating the variable to false should not be the responsibility of these functions - The flagValidation variable being globally scoped and mutated/used in several places leaves a lot of places for it to fail when making changes - The functions are doing too much. When looking at it from a functional standpoint, they should just be returning a bool, and a final validate function can follow up. - Repeating code (e.g. document.getElement...) unnecessarily When you allow your functions to be purely functional, this function...

function PhoneNumberValidation() {
var phoneNum = document.getElementsByName("Phone")[0].value;
var normalPhonepattern = /^[0-9s-+]{6,14}$/g;
if(!normalPhonepattern.test(phoneNum))
{
flagValidation = false;
document.getElementById("PhoneValidation").innerHTML = "Only numbers, '-' and '+' characters are accepted"
}
else
document.getElementById("PhoneValidation").innerHTML = ""
}

Can become...

function isPhoneNumberValid() {
const phoneNumber = document.getElementsByName("Phone")[0].value;
const phoneNumberRegex = /^[0-9s-+]{6,14}$/g;
return phoneNumberRegex.test(phoneNumber);
}

Much prettier, right? Once we've refactored all of those individual functions, the main input validation function looks like this:

function validateFormInputs(event) {
let isFormValid = true;
const phoneNumberFeedback = document.getElementById("PhoneValidation");
if (isPhoneNumberValid()) {
phoneNumberFeedback.innerHTML = '';
} else {
phoneNumberFeedback.innterHTML = "Only numbers, '-' and '+' characters are accepted";
isFormValid = false;
}
if (isFormValid) {
contactForm.removeEventListener('submit', validateFormInputs);
return true;
} else {
event.preventDefault();
}
}

It's cleaner, sure, but I'm still not okay with using and mutating that isFormValid variable and innerHTML appearing every other line. Let's take it further. Let's outsource the error message work to a utility function.

function generateErrorMessage(element, message) {
return element.innerHTML = message;
}
// So we use that like this...
if (isPhoneNumberValid()) {
generateErrorMessage(phoneNumberFeedback, '');
} else {
generateErrorMessage(phoneNumberFeedback, 'Cannot be empty');
isFormValid = false;
}

The next step is to stop mutating that validity flag. To do this, I'm going to bundle all the validation methods into an object and then reduce that to return an isFormValid bool.

const fields = {
phoneNumber: {
isFieldValid: function() {
const phoneNumber = document.getElementsByName("Phone")[0].value;
const phoneNumberRegex = /^[0-9s-+]{6,14}$/g;
return phoneNumberRegex.test(phoneNumber);
},
userFeedbackElement: document.getElementById("PhoneValidation"),
errorMessage: "Only numbers, '-' and '+' characters are accepted"
}
};
// Generate an array from the keys of the methods object and reduce
Object.keys(validationMethods).reduce((acc, curr) => {
// do stuff
}, true);

If you're not familiar with Array.reduce, it will iterate over each item in the array and allow you to process them. The arguments are acc (accumulative) and curr (current). The idea is, we're going to execute each function and then show/hide error messages accordingly. The function now looks like this:

function validateFormInputs(event) {
const isFormValid = Object.keys(fields).reduce((acc, curr) => {
const currentField = fields[curr];
if (currentField.isFieldValid()) {
generateErrorMessage(currentField.userFeedbackElement, '');
return acc;
} else {
generateErrorMessage(currentField.userFeedbackElement, currentField.errorMessage);
return false;
}
}, true);
if (isFormValid) {
contactForm.removeEventListener('submit', validateFormInputs);
return true;
} else {
event.preventDefault();
}
}

This implementation is clearly a case-by-case basis. It works for my particular scenario because there's only one validation condition for each field. If there were more rules, the approach would need to be changed to compensate and it may not be able to be as dynamic. It should also be noted that this is a fairly over-engineered solution. I wouldn't say that the original approach is wrong, but my approach looks at the same problem from a functional programming standpoint and I believe it is much cleaner and much more robust. For a view of the entire file, see my gist at https://gist.github.com/3stacks/c5c49904684e4ddec48aa017ab912db9

I have had my eyes on Argus Eyes (http://arguseyes.io/) for quite some time and now I have the time to implement it at work. The interface is rather simple. You define your browser breakpoints, the pages, and the parts of the pages you wish to capture. All components are defined with a name and a selector. For example, ".site-nav" or "body". You define all components in the components array, but then you can cherry pick which ones are used on each page. Such as, homepage may use the hero component, but about may not.

{
"sizes": [ "320x480", "1280x768", "1920x1080" ],
"pages": [
{
"name": "homepage",
"url": "http://localhost:3000/",
"components": [ "hero", "all" ]
}
],
"components": [
{
"name": "all",
"selector": "body"
},
{
"name": "hero",
"selector": ".hero"
}
]
}

Since I'm generally against installing npm packages globally (and you probably should be too), I define my capture scripts in package.json. This presents the first issue: The usage of Argus is like so: argus-eyes capture <branch-name> But this of course only names the capture for you. It's your responsibility to switch branches. So the workflow becomes:

  • Clone develop branch
  • run argus-eyes capture develop (this is the baseline)
  • Clone feature-branch-name
  • run argus-eyes capture feature-branch-name
  • run argus-eyes compare develop feature-branch-name

Argus then uses blink-diff to compare the two sets of screenshots you just captured (note, you shouldn't change your config between captures) and outputs any screenshots in which there are visual differences. For example, bumping the padding on your nav will result in something like this. It's not a super intelligent representation, however, it does quickly show you that something is wrong. In my opinion, the current workflow makes it almost worth not bothering. So how do we make it a 1 step test?

Automation

I am attempting to simulate this entire process in node. For this, we'll need a few things.

I've tried to make the node script as pure as possible. I created a file called argus-test.js. In that, there is an individual function for each git action. First is a function to initialise the repo.

/**
* @param {string} path - path to the repository (.git)
* @returns {Promise}
*/
function openRepository(path) {
return Git.Repository.open(path);
}
// Path is based on current working directory
const repoPath = require("path").resolve("./.git");
openRepository(repoPath).then(...)

openRepository returns a Promise which has the reference to the repository in it. To act on the repository, we need to keep track of this returned value. Since all of the nodegit functions return Promises, we're going to be seeing a lot of then.

// Initialise this let to keep track of which branch we're on
let featureBranch;
/**
* @param {Repository} repo - The reference to the repository object
* @returns {Promise}
*/
function saveCurrentBranch(repo) {
return repo.getCurrentBranch();
}
openRepository(repoPath).then(
repo => {
saveCurrentBranch(repo).then(
repoName => {
featureBranch = repoName
})
},
err => {
// Usually would only happen if you give it the incorrect path
throw new Error(error)
}
);

Now we have a reference to the current feature branch, we've got that stored for later. In the function where we set the featureBranch variable, we're going to execute our capture functions.

shell.exec(`node node_modules/argus-eyes/bin/argus-eyes.js capture ${featureBranch}`);
// Successful output will say something like "12 screenshots saved to .argus-eyes/feature-branch-name"

This is the tricky part. We have to switch branch to whatever the base is (develop in this case). This is the biggest hurdle. Although the function is simple, if there are any uncommitted changes, the function may fail. Probably best to warn the user to make sure all changes are committed or stashed first.

/**
* @param {Repository} repo - The reference to the repository object
* @returns {Promise}
*/
function switchToDevelop(repo) {
return repo.checkoutBranch('develop');
}
switchToDevelop(repo).then(...)

After successfully changing to develop, we still have to capture the branch and then compare them, which is done like so:

shell.exec('node node_modules/argus-eyes/bin/argus-eyes.js capture develop');
shell.exec('node node_modules/argus-eyes/bin/argus-eyes.js compare develop ' + featureBranch);

If Argus detects any screenshots over the threshold for change, it will save the diff in a folder like .argus-eyes/diff_develop_feature_branch_name For the full file in action, check out this gist: https://gist.github.com/3stacks/0976ef8a84c50c6096aea09dbbbebd88

Retrospective

To improve this process, it might be an idea to save the baseline diff in the repo and then overwrite it whenever you push to that branch. This would eliminate the need to switch over the branches.