(If you remember from last time, the WiH in the title means this was “Written in Haste” i.e. not heavily edited, so keep that in mind as you are reading.)
First, a quick summary from Designgineering Chronicles: The First Month in case you missed it (lots of good stuff in there if you want to go back and read, though):
- PMC (Penske Media Corp), where I am currently a contract Design Engineer, is the umbrella company for several large, mostly entertainment brands with websites on WordPress VIP. They all have completely different front-ends that were built by outside agencies, then maintained in-house.
- In my first month, I started out with some general process research via informal interviews, tested out ideas for improvements in front-end workflow (including splitting up tickets into front-end and back-end), and started strategizing for a design-system-type-thing as I learned more about how PMC works.
- The first development initiatives I planned are a style guide for Indiewire’s “homepage refresh” and a plan for standardizing the build step among PMC’s brands.
I ended that post with a section titled Slow down, brain…a sentiment perhaps even more relevant to month number two.
A lesson learned
Up until this position, I’ve worked either independently or for small agencies where operations are much leaner, and implementing a new initiative or workflow across the company can happen, if not overnight, almost. This is not the case at a large company, and that’s for good reason. Process should ensure that sweeping changes happen in a way that is incremental, intentional, and at a pace allowing for everyone involved to adjust, provide input, and learn about the change taking place.
Just imagine the chaos if anyone – namely a chipper, overenthusiastic contractor – with a great idea could go off on her own and implement huge, company-wise changes without 1) getting input from those affected by the changes, and 2) allowing time for the ideas to vet themselves naturally. Ahem.
Soon after my last post, I presented a proposal for what I dubbed “Front-end Ops” at PMC. The outline included a plan for standardizing the build step, a strategy for collecting and deploying reusable UI components via npm, and the implementation of living style guides within themes as a way to develop front-end separate from the often cumbersome Vagrant environment. The response from the tech leadership was very positive, and everyone was on board.
I’m not sure what I thought was going to happen after that – maybe that I would be excused from my business-as-usual tasks to go into a corner to focus on “Front-end Ops”, full-time? To be honest, yes, I think that was my expectation. If I, the overenthusiastic contractor – well, right amount of enthusiastic – had been allowed to go off into a corner, a few bad things would happen:
- All of the knowledge about what I’m building would be in my brain. What would happen if I left? This known, darkly, as the bus factor, and is very dangerous.
- I would be missing out on the knowledge and input from everyone else on my team. I may have the most experience in this domain of knowledge, but everyone is going to use what I build, and most of them have more experience at both PMC and in software at large.
- Let’s be honest: my individual track record for seeing grandiose plans through to the end isn’t great. Sure, I might have pulled through in the end, but there is an equal (probably greater) chance I would have horribly underestimated the work and lost steam part way through.
- The work wouldn’t be tracked, and valuable knowledge regarding how the project was executed would be lost. PMC operates under a very tight, Agile project management workflow that requires detailed tickets for every development task. Why should this be different?
Robin Rendle writes about a similar awakening in Design Systems at Gusto, and I’ve seen countless tweets and Medium articles (the links to which are escaping me at the moment) warning of the dangers in the “hero programmer”. Luckily, the process and project management in place at PMC keep that from happening! Whew.
It might be frustrating when I can’t always work on what I want when I want (and it’s not like I did that before this job anyway…), but that’s a trade-off for working on a team and on bigger projects. It doesn’t work for one person to just pick up and switch gears at the drop of a hat. Lesson learned.
That being said…I did kinda go off into a corner to build something.
Indiewire’s Pattern Library
Around this time I was reading Brad Frost’s Atomic Design, where he outlines the steps to implementing a design system at large companies:
- Make a thing
- Show that it’s useful
- Make it official
That thing is Indiewire’s pattern library. Like I mentioned in the first Designgineering post, one ongoing project is the refresh of Indiewire’s homepage design. I took over the tasks for one sprint, and ended up churning out a pattern library and rockin’ CSS architecture in about a week and a half. Here are some notes about the tech side:
ITCSS 💜
I decided to use a modified version of ITCSS with the addition of layouts
from SMACSS and a separate prefix g-*
for specific grids. I decided to give layouts their own prefix because I think they can eventually be pulled out into patterns that apply to more brands than Indiewire, and a few of the other brands’ websites built by outside agencies use layouts, so the concept is familiar. Similarly, the g-*
rules are progressively enhanced grids that will be abstracted to mixins that will then be ported over to other brands.
I’ve made great use of custom properties for adjusting values in object
modifiers. Objects
are essentially molecules and components
are essentially atoms. Objects should define margin/padding and font-sizes for components according to their context. o-*
and c-*
classes often appear on the same element. For example:
<article class="o-tease">
<h3 class="o-tease__title c-title">‘Kusama: Infinity’ Tops New Releases, but the Real Specialty Box-Office Story Is ‘The Wife’</h3>
<p class="o-tease__dek c-dek">Also: “Blaze” hits New York with strong results, while “The Little Stranger” goes down with a whimper.</p>
</article>
In this instance, the c-*
classes provide declarations for properties like font-family
, color
, and line-height
, and the o-*
classes are responsible for context-dependent styles like font-size
, margin
, and padding
.
Phrased another way, .c-title
‘s font-size
comes from a general selector (e.g. h3
) unless a custom property is set within it’s context, e.g. an o-*
selector. Then, modifiers on the context update the custom property value and there are only ever two font-size
declarations on .c-title
. This way, .c-title
and .c-dek
can be reused in lots of different contexts and the specificity remains very low. It’s been working well so far!
Let’s look at a snippet of the SCSS for .o-story
and .o-story__title
which has several modifiers that that update the font-size
that is bound to .o-story__title
:
@supports( --color: blue ) {
.o-story {
--storyTitle__FontSize: #{rem-calc(18)};
--storyTitle__DekSize: #{rem-calc(15)};
}
.o-story--featured {
--storyTitle__FontSize: #{rem-calc(24)};
@include breakpoint( tablet-min ) {
--storyTitle__FontSize: #{rem-calc(30)};
}
}
.o-story--river {
@include breakpoint( tablet-min ) {
--storyTitle__FontSize: #{rem-calc(24)};
--storyTitle__DekSize: #{rem-calc(18)};
}
}
}
// ... other rulesets removed for brevity.
.o-story__title {
font-size: rem-calc(30); // Fallback.
margin-bottom: rem-calc(10);
margin-top: rem-calc(10);
@supports( --color: blue ) {
font-size: var(--storyTitle__FontSize);
}
}
.o-story__dek {
font-size: rem-calc(15); // Fallback.
@supports( --color: blue ) {
font-size: var(--storyTitle__DekSize);
}
}
Minimal nesting
Another note about SCSS: I’ve opted to not nest BEM selectors and this has done wonders for readability. Harry Roberts mentioned as a no-no (can’t find the link at the moment), and for these projects, I agree. The biggest factor is the inability to search for a full selector – something quite essential in a large SCSS code-base.
A tests
section in the Pattern Library
I selected kss-node for generating the style guide and Twig for templating (I just love embed
s and including context variables – Nunjucks supports this with the macro
interface, but I like the more markup-y style of embeds vs. function calls like macros). The pattern library examples are wonderful for code documentation, but they don’t always make sense form a product standpoint.
For example, o-tease
and it’s modifiers don’t look like much by themselves – they only provide some general structure. The patterns only make sense from a product perspective when they are assembled and working together. To solve this, I created a set of Twig partials called tests
. These are assemblages of patterns that directly represent the design requirements, and are very useful for testing out the patterns before implementing them in the PHP templates.
For example, this is an excerpt section from a test
that applies several modifiers and additional patterns to o-story-list
:
{% block features__related %}
{% embed "../5-components/_o-story-list.twig" with {
modifier_class: 'o-story-list--horizontal o-story-list--no-separator g-3-pack u-bg-dots u-bg-dots--half@desktop'
} only %}
{% block story_list__item %}
{% embed "../5-components/_o-story.twig" with {} only %}
{% endblock %}
{% endembed %}
{% endblock %}
Now that I’m looking at this with fresh eyes, it does seem like a lot of nested block
s and embed
s. Oh well, it’s a work in progress!
What about Pattern Lab or Fractal?
Pattern Lab and Fractal are ready-to-go environments for creating pattern libraries, and in some ways, I am re-inventing what they already do. However, I’m making the judgement call that PMC’s needs are unique enough and at a scale that deems starting from scratch and building up as needed is the best path forward.
For example, one possibility for reducing markup redundancy would be to adapt PMC’s render_template
PHP function to output markup into the pattern library from the template used in production. This type of structural change, I believe, would be more challenging if we were working within an existing architecture.
Stepping away
In keeping with my lesson learned as described above, the next step for the pattern library is for me not to work on it. There are several tasks coming up that will involve the addition of new components, and I’m looking forward to sharing the knowledge with the other engineers on my team and improving the library with their ideas. It’s a bit sad because I just love working on this stuff, but it’s not useful if only I can do it!
A note about tickets
One thing that’s been a little bit of a failure is separating tasks into front-end and back-end, and I think that’s because 1) it depends on the task and 2) separate JIRA tickets might not be the right medium for the division. It turned out this was quite confusing for the product folks that needed to approve the work – e.g. it would be difficult and unnecessary for them to QA un-styled HTML. Also the branching was a bit confusing, and it seemed to create unnecessary dependency between tasks.
We are now testing out JIRA sub-tasks, which I did not know existed! So far that has proved to work well, but part of the value in dividing the tickets was that two developers can work on the task simultaneously; one on front-end and one on back-end. This could still be possible with a shared branch, as long as we are willing to handle the conflicts when they arise…
The Build Step
Indiewire is also proving to be a great test bed for the build step. My initial thought was to use npm scripts and create a simple Node setup using shelljs
to build the commands in a DRY way (basically storing strings in an object and concatenating them according to certain parameters), but at the encouragement of my excellent manager, I dived into using the actual JS API for node-sass
. This has resulted in alarmingly fast build times (it’s basically a direct line to the C++ compiler in Libsass now) and a setup with very few dependencies. Several of the engineers are on Windows and run Node through the Vagrant machine, so it is important to minimize potential environment headaches.
This post is getting quite long, so I will just post a snippet of the in-progress Sass dev
command:
const dev = () => {
const graph = grapher.parseDir(directories.sass.src);
console.log( chalk.blue('Running the dev task. Assets will be compiled on save.') );
gaze('./src/scss/**/*.scss', function( err, watcher ) {
// What to do when a watched file changes (watcher === this).
this.on('changed', function(file) {
// Compile file if it is not prefixed with an underscore,
// otherwise, refer to the ancestors of the changed file in the
// Sass dependency graph and compile the relevant ancestors.
if (path.basename(file)[0] !== '_') {
compileSass(file, outFilePath(file), 'dev');
} else {
graph.visitAncestors(file, (f) => {
if (path.basename(f)[0] !== '_') {
compileSass(f, outFilePath(f), 'dev');
}
});
}
});
// This should log when a file is created – currently it does not.
this.on('added', function(filepath) {
console.log(filepath + ' was added');
});
});
};
Yes, this is absolutely re-inventing the wheel. But again, it’s okay for PMC’s purposes, and building a watch task for Sass took me all of an afternoon. If/when something breaks down the line, myself and others will be able to find exactly what went wrong without hunting through node_modules
.
Working in Node is very new to me, and I’m excited to continue exploring! I’ve broken this project down in to several detailed tickets so that – again in line with my “lesson learned” – others on my team will be able to contribute.
What’s next?
Like above, what’s next for me is stepping away from this work, somewhat, and ensuring others can do what I’ve been doing by writing detailed tickets, documentation, and exploring how to test the npm tasks. Also on the topic of testing, there is a ton of work to be done in writing PHPUnit tests for the existing codebases, and I’m looking forward to learning more about that.
Okay, this post is long and I’m hungry. Hopefully I will be organized enough to write one next month, too! It’s very helpful to have this ritual for reflecting on the job and I think will prove beneficial in the long run to have my experience documented, for myself and for others.