Using Tailwind with Begin.com and Architect

10/23/2020 Tailwind, Architect, Begin.com

Using Tailwind with Begin.com and Architect(arc.codes) is mostly straightforward with just a few hurdles. The instructions below cover how to use them together for a server rendered scenario. This is not a full introduction to either Tailwind or Begin/Architect so it will be helpful to be familiar with both before reading this post.

TL&DR

The biggest challenge with the integration is building the production vs development CSS and connecting that to the Begin.com or Architect deployment process. Skip to that section if you are already familiar with Tailwind and Begin/Architect separately.

Install Tailwind

Refer to the tailwindcss.com docs for details on this, but once you have a Begin or Architect project setup install tailwind at the root with the following:

# Using npm
npm install tailwindcss
# Using Yarn
yarn add tailwindcss

Inject Tailwind styles

Add the tailwind directives to your CSS stylesheet if you have one. If you are styling the entire app with tailwind create a tailwind.css with the three directives below. Put these styles in the src/styles directory. They could also live at the root of the project to avoid adding more files to the src folder.

/* /src/styles/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Other global CSS styles here*/

Tailwind Config File

When you install tailwind you create a config file with the following command:

npx tailwindcss init

The important step for Architect and Begin is to point to any src files that contain any tailwind classes so that the production build will not purge any of the classes needed. The example below looks at any javascript, html, and njk(nunjucks template) files for tailwind utility classes.

// tailwind.config.js
module.exports = {
future: {},
purge: ['./src/**/*.js', './src/**/*.njk', './src/**/*.html'],
theme: { extend: {} },
variants: {},
purge: [],
plugins: [],
};

Build CSS for Development vs Production

Tailwind has a development build that is huge (i.e. 3-5mb). It includes everything so that you can add and remove utility classes while developing without rebuilding CSS. When you build the CSS for production it purges everything that it doesn't need to reduce the CSS to a more reasonable kb size. The production build should be regenerated every time you deploy in case any new utility classes need to be included.

Below is the package.json file that will work for both Begin.com and Architect.

{
"name": "begin-app",
"version": "0.1.0",
"description": "Tailwind example",
"scripts": {
"start": "npm run check-css; npx sandbox",
"build": "npm run build:prod-css",
"staging": "npm run build:prod-css; arc deploy",
"production": "npm run build:prod-css; arc deploy production",
"check-css": "if !(tail -n 1 ./public/css/styles.css | grep 'DEVELOPMENT BUILD'); then npm run build:dev-css; fi;",
"build:dev-css": "NODE_ENV=testing npx tailwindcss build ./src/styles/tailwind.css -o ./public/css/styles.css -c ./tailwind.config.js &&echo '/*DEVELOPMENT BUILD*/' >> ./public/css/styles.css",
"build:prod-css": "NODE_ENV=production npx tailwindcss build ./src/styles/tailwind.css -o ./public/css/styles.css -c ./tailwind.config.js"
},
"devDependencies": {
"@architect/sandbox": "^3.0.5"
},
"dependencies": {
"tailwindcss": "^1.7.5"
}
}

When npm run build:prod-css is run Tailwind will purge the CSS if NODE_ENV=production. When the build:dev-css is run all styles are included. Both these scripts output to the same file ./public/css/styles.css. There is currently not an easy way to deploy a different asset (i.e. styles.css) depending on what stage you are running which is why both build to the same file. The development script tags the bottom of the output file with a comment /*DEVELOPMENT BUILD*/. This is used to detect if it needs to be run again to avoid rebuilding the development file every time you launch the local sandbox.

Begin.com CSS Build

Begin.com is a CI/CD service that runs when you commit your changes to your main branch on Github. As part of the deployment process it automatically runs a build script defined in your package.json. This is not configurable so you have to use build. This is the best place to include your production CSS build step. The build script in the above package.json calls the build:prod-css script.

With Begin you don't need to generate your production CSS locally because it is handled on Begin.com. This eliminates overwriting development CSS locally every time you deploy.

Architect (arc.codes) CSS Build

With an Architect project you can deploy to Staging or Production with the commands arc deploy or arc deploy production directly from a local environment. It will then use the static assets in your ./public folder which include your CSS. For this reason you need to generate the production build just prior to running those deploy scripts. That is handled by the staging and production script above.

If you are deploying directly from a local environment you will then need to rerun your development build the next time you try to launch the sandbox locally. This can take 10 seconds in my experience so you don't want to do it unless you have to. The check-css script looks at the last line of the CSS file to see if it is tagged as DEVELOPMENT before rebuilding.

Fingerprinting of CSS assets

Architect can handle fingerprinting of static assets to help browsers and CDN's properly cache your CSS. Below is the Architect config file for this project. The @static pragma is what configures the static assets. The folder public tells Architect which folder you put the assets in. Setting fingerprint true means that the styles.css file generated will be appended with a hash that is unique for the file contents similar to styles-8ax5rs.css. Architect will also add aggressive caching headers to make sure that if the CSS doesn't change it will be cached as long as possible.

app.arc

@app
link-pages
@static
fingerprint true
folder public

Pointing to CSS from HTML

Especially with fingerprinting, but even without it you need a way to point to the correct place for your static assets. Architect provides a helper function to make this easier. Below is a sample lambda file that serves HTML. This includes the CSS in the header using the arc.static('css/styles.css') helper to point to the correct path and the fingerprinted filename for the CSS.

const arc = require("@architect/functions");
async function page(req) {
return {
status:200,
body: `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet"
href="${arc.static('css/styles.css')}">
</head>
<body>
<div class="bg-white text-bold">
A Page of Links</div>
</body>
</html>`
}
exports.handler = arc.http.async(page);

Gatsby or other Front end build systems

The above process works best when sending server rendered html from Architect or Begin.com. When using Gatsby or some other frontend framework to build an app it often works best to follow the installation instruction for adding tailwind to Gatsby for instance. It is then built by Gatsby and included in the public folder along with everything else.


© 2020 Ryan Bethel, Built with Gatsby.