February 11, 2019

Plugin Base Styles and Shared Theme Values

I worked a lot less than usual due to a nasty cold last week but still managed to make pretty good progress on a couple of things for Tailwind 1.0.

Support overriding a core plugin's configuration

In Tailwind 1.0, core plugins will be disabled by setting them to false in the corePlugins section of your config file:

module.exports = {
  // ...
  corePlugins: {
    float: false,
  }
}

When I came up with that solution it also occurred to me that it could make sense (at least conceptually) for this corePlugins section to also be where end-users could completely override the configuration for a core plugin if they needed to:

module.exports = {
  // ...
  corePlugins: {
    opacity: {
      variants: ['responsive', 'hover'],
      values: {
        '0': '0',
        '20': '0.2',
        '40': '0.4',
        '60': '0.6',
        '80': '0.8',
        '100': '1',
      }
    },
  }
}

There's really no reason at all for someone to actually make use of this right now (all of that configuration is better exposed through the theme and variants sections) but I liked that this config file design supported it in theory, in case it's ever useful in the future.

I submitted and merged a pull request (#644) for this early in the week.

Drop legacy browser support in cssesc and postcss-selector-parser

A few weeks back I wrote about how I was really excited to take advantage of some changes in postcss-selector-parser that would make escaping class names in Tailwind a lot simpler.

Unfortunately I wasn't able to do that because of a bug in css-loader that, when combined with the cssesc library support really old versions of IE, resulted in the : character being improperly escaped when Tailwind was processed in a webpack context.

Last week I was able to get two PRs (#19, #169) merged that dropped support for IE < 8 in both cssesc and postcss-selector-parser which means I should be able to simplify some things around class name escaping once a new postcss-selector-parser release is tagged.

Allow Tailwind plugins to register base styles

I added a feature (#649) to Tailwind's plugin system that lets third-party plugins register base styles using a new addBase helper:

module.exports = {
  // ...
  plugins: [
    function({ addBase }) {
      addBase({
        'img': {
          maxWidth: '100%',
        },
        'button': {
          fontFamily: 'inherit',
        },
      })
    }
  ],
}

This makes it possible for people to author plugins that provide different reset styles than our existing preflight styles, or add other useful base styles like default typography styles for example.

Part of adding this feature included defining a new @tailwind base directive where any plugin-defined base styles get rendered.

Convert preflight into a core plugin

Once I made it possible for plugins to register base styles, I worked on converting our preflight styles into a core plugin (#650).

This meant I could remove the @tailwind preflight directive and make @tailwind base the one-true way to render any base styles in your CSS.

Allow theme values to reference other theme values

One of the things I've always hated about the 0.x Tailwind config file is the colors variable we define at the top purely to make it possible to reuse those values for textColors, backgroundColors, and borderColors.

For Tailwind 1.0 I wanted the colors key in the theme section of the config file to be automatically respected by the other color-dependent plugins instead of having to use a variable to avoid the duplication.

I had a few false starts on this (#639 and #643) but eventually decided to solve it by making it possible to define theme values as closures so they were lazily evaluated (#645):

module.exports = {
  // ...
  theme: {
    colors: {
      'transparent': 'transparent',
      // ...
      'pink-lightest': '#ffebef',
    },
    backgroundColors: theme => theme.colors,
    textColors: theme => theme.colors,
    borderColors: theme => {
      return global.Object.assign({ default: theme.colors['grey-light'] }, theme.colors)
    }
  }
}

There's potential for circular references and infinite recursion here but not terribly worried about that and comfortable making that the end user's responsibility to avoid.

Picking up work on SponsorShip

Last year I had an idea for an app to help me manage sponsorships for my podcast, and decided to try and build an MVP in a single day (ended up taking two), live streaming the whole thing on YouTube.

I didn't take it any further than what I'd done on the live streams, and never really thought about it much after that for a few different reasons:

  1. I was sort of burnt out on the project after grinding through two full days of live-streaming.
  2. I needed to focus on the Refactoring UI book.
  3. As part of scoping the project down to be buildable in one day, I had sort of subconsciously convinced myself that it was a boring, simple one-screen app, and there wasn't anything terribly interesting to be learned from seeing it through to the finish line.

As I've mentioned in other updates, one of the things I struggle with a lot is figuring out ways to keep my application development skills sharp, because most of my time is spent doing educational stuff, or working on Tailwind which is just a library.

I've been trying to find a good side-project to work on that I could use to keep improving as an actual software product developer as well as dogfood Tailwind, but most of the ideas I've come up with have had annoying flaws with them.

BarelyAccounting, the simple bookkeeping software I wish I had for running my own business, is a pretty good idea as far as the feature set is concerned, but I really want to build something I can actually throw on the internet and have real people use, and I'm not sure I'm comfortable having a bunch of people's important financial data in my database, both from a compliance perspective and a general terrifying responsibility perspective. These days my books are handled by Bench (disclaimer: that's my referral link) anyways, so this isn't really even a huge problem for me anymore.

DevJournal, the online community I wanted to build for other developers to journal about their work like I do here, would still be a useful thing to build and I'd like it to exist, but I don't think there is a lot of interesting stuff involved in building it. It's a really simple CRUD app for people to basically publish blog posts. I'd still like to build it one day, but as my main side-project I'm not convinced it's the best opportunity.

SponsorShip on the other hand is more interesting than I remembered. I know quite a few people in my own network who could actually benefit from it, and although the original MVP was this super simple single-screen app, I realized after looking at it again that that wasn't because that was the best way to build the product, it was really just because I wanted to build it in a single day.

There are a lot of interesting features I could build and I think it has just the right amount of real-world potential that I'm excited to dive back in to it. I've already scheduled a call with one potential customer to talk through the idea a bit and do some planning, so hopefully I can spend a full day or two digging back in to the app next week.

One thing that is important for me to remember is that even though I'd like whatever side-project I end up pursuing to be a very real-world SaaS app, I'm building it primarily for the benefit of my developer audience, even if the actual customers of the app are an entirely different group (like people with podcasts/newsletters/etc in the case of SponsorShip).

Put another way, the real reason I'm building an app isn't for the app to be successful and make money (although I think it's important that I pick an idea that has that potential, because it makes the project more realistic) — it's so I can continue to discover new ideas that I can teach to other developers, to give me something interesting to live stream for my audience to learn from, and to have a really polished codebase I can open source for others to study.

Plans for this week

  • Record and release a new Full Stack Radio episode, going to talk to Jonathan Reinink about an interesting approach he's been using lately to build server-side Laravel apps while also getting most of the benefits you'd get with an SPA.
  • Update the Tailwind CLI tool to better accommodate the new config file format
  • Update preflight to use normalize.css as a proper npm dependency, instead of manually maintaining a copy-and-pasted version like we do now
  • Make some decisions around core plugin and theme key naming, really struggling to decide if I should make everything match CSS property naming more closely (textSizes would become fontSize) and if I should standardize on singularizing everything (backgroundColors would become backgroundColor)
  • Release Tailwind 0.8, which will serve as a partial preview of 1.0 to help spread some of the breaking changes out a bit
  • Update any Tailwind documentation that needs to change for the changes in 0.8