logo

Josh Clow

Software is a human craft

Tools and Tradeoffs

March 11, 2020

One of the consequences of using the set of tooling I did to build this site is that, as is usual with tools in software, I had to make tradeoffs between “this would be the way I would solve this problem myself” and “this is the way the tool wants or expects me to solve the problem” and this led to some cases where I backed off on a given tool, and others where something that seemed like it should be easy was anything but, but the benefits were worth the complications anyway. So I thought I’d run through some examples.

Typography.js and Theming

The blog starter I used to build the initial skeleton included use of the Typography.js library. This library is built to support theming around font choice, spacing and whatnot. In many ways, it’s somewhat analogous to Wordpress Themes. I really liked the look of one of the prefabs here and so incorporated it. Unfortunately, I immediately encountered problems. The main issue is that it doesn’t really play comfortably with Emotion.js which I wanted to use for styling my React components. Since Typography.js operates on Javascript objects, my initial hope would be that I could pull those out into shared files, and then pass them into Typography’s customization system and into styled calls for emotion. I think that could work, but Typography.js sort of expects to use specificity rules to control styling, while Emotion.js uses generated classnames to control styling, which is a philosophical mismatch that experience has taught me will likely lead to unpleasant to debug styling issues. Further, the actual styles in a given Typography.js theme are somewhat opaque (I mean, it’s just a npm module, so it’s not like it’s a secret or anything, but in a design sense, it’s hard to compose with it, because it’s hard to know what the base behavior is).
So given that I wanted to make any of a number of subtle tweaks to spacing and font use here and there, I decided to abandon Typography.js entirely and extract the theme details so I could use them directly in my code. All that said, I think as a “let me drop a configuration in and get a good looking site” kind of turnkey solution, Typography.js is really good and I would recommend it to someone who didn’t want/need to tinker as much as I wanted.

Emotion.js theming

Emotion.js has some features to support theming as a concept. They’re less comprehensive than Typography.js, but because of that, they’re less opinionated and possibly a better starting point if you want to build out your own theme system.
The truth is, if you’re familiar with React, Emotion’s theming support is mostly just some convenience support around React Context. The idea being that you specify a theme object as a property on a Provider component, and then you can access that object through a prop, HOC, or hook depending on the use-case, without having to keep passing the theme around on every intermediate component.

What’s less obvious is how this ends up with styled components. My preference with Emotion is to use the styled component model, which ends up looking like this:

const StyledDiv = styled.div({
    backgroundColor: "pink",
    width: "100%",
});

I like this pattern for CSS-in-JS libraries because I find it pretty easy to read, and the object syntax means composing styles is straightforward in a way that passing strings makes complicated (plus, I find it’s easier to get good syntax highlighting and lint/formatting support with objects than it is with strings of CSS snippets).
The problem with Emotion’s theming and this pattern is now you have to get the theme data into the styled call. Which looks something like this:

const StyledDiv = styled.div({
    width: "100%",
}, ({theme}) => ({
    backgroundColor: theme.colors.background,
}));

I have problems with this. The main one that the arrow function rules mean there’s a bunch of parens and brackets sitting there in close proximity and they all mean slightly different things. It’s unambiguous to a transpiler or interpreter, but it sure as hell isn’t great to read.
For now, I’ve left this kind of gross thing in my code, but I want to revisit this, probably when I add an actually interesting second theme (I’m thinking light/dark is the obvious split). The right answer may simply be to write a theme-aware styled helper, but I want to think about it carefully, because it won’t be the last time I have to deal with this in React.

The CMS

I’m using netlifycms for my content. Gatsby has starters that integrate it, and the way it uses git as the backing store is really, really clever. There are some wrinkles though.
The first wrinkle is about authentication. The basic idea is that netlify cms includes an administrative page for working with content (including an editor for writing posts), and since you don’t want to let just any random person use that, and you need credentials to push changes into your git host anyway, the standard approach is to use oAuth with your backend of choice (GitHub, GitLab, etc.)
Unfortunately, GitHub specifically doesn’t support the oAuth feature needed if you want all this to work entirely from the webpage. If you deploy and host your site through Netlify’s main product, then it can handle the details for you and the whole thing becomes very turnkey for GitHub.
But I have a host, and part of this exercise was about building my own build/deploy pipeline since that’s stuff I’ve been a little weak on. So that meant I needed to do this the hard way. Specifically, I needed an application running on the Internet somewhere that could broker the oAuth handshake between my site and GitHub. This required building an AWS Lambda (there are obviously other solutions, but I went with this one to get my hands dirty in the AWS space). The tricky bits there deserve their own post, so I’ll detail them later.
Perhaps the more confounding problem with netlifycms is just how hard it turned out to be to style the preview window for the editor, so that I could see what a post would look like before publishing it, complete with my preferred fonts, colors, etc.
The problem is that the CMS code accepts styles for this exact scenario…by way of a CSS file or a string representation of the CSS. Complicating matters further, the standard plugin for Gatsby to use the CMS wants you, very specifically, to import a CSS file into a Javascript module and then specify that module in the configuration for the plugin. Then, at build time, it extracts the styles, puts them somewhere the Netlify code can find them, and you get your styled preview.

Uh oh.

Absolutely nothing about that plays nicely with Emotion. Looking around, I was able to find some solutions where people had used Emotion’s Server Side Rendering support to get the styles into a string for the CMS, but that required manipulation of the Webpack config to do, and Gatsby abstracts much of that away. I was afraid I was going to have to write a bespoke Gatsby plugin (which is the preferred way to modify Webpack configuration in a Gatsby build). Fortunately I stumbled across an alternative solution.
Netlifycms offers an API in which you can provide your own preview components for previewing a given “type” of data. The Gatsby plugin more or less provides direct access to this API because it would cut off a bunch of important functionality to not have it. So the answer here turned out to be to tell the CMS that I wanted to provide a custom previewer for the “Blog” type of content, thereby entirely replacing the default preview with my own React component. That component was free to use Emotion (though since the preview is hosted in an iframe, there’s some additional excitement to get the styles into the right <head> element, but this is easier in current versions of Emotion than it was in the past), and much to my relief, the individual previewer components for things like “Title” and “Body” were available to me via the CMS api, so I could simply build a component that added styles and then rendered the exact same preview data that the default preview would have.

This was a ton of work for something that felt like it should be a mainstream scenario.

But, in this case, I elected to take this solution instead of backing off on the tools, because the CMS is very useful, and I really don’t want to roll my own, try to bring in a different one, or revert to making all my posts via git commits. And I don’t want to drop the official Gatsby plugin here because…I looked at the code and, frankly, that’s not something I’m quite ready to try to pull apart and build myself.

Thanks for sticking with this long post! I hope this gives a taste for how complex it can be to bring in a “simple” library or framework, and how doing so often creates problems as big or bigger than the ones you were trying to solve in the first place.