The Things I Add to Tailwind CSS Right Out of the Box

In every project where I use Tailwind CSS, I end up adding something to it. Some of these things I add in every single project. I’ll share these with you, but I’m also curious what y’all are adding to your tailwind.css files.

I’ll start with myself. In each project:

  • I define -webkit-tap-highlight-color.
  • I add a bottom padding set to env(safe-area-inset-bottom).
  • I dress up unordered lists with interpuncts.

Allow me to elaborate on all three.

-webkit-tap-highlight-color

Android highlights taps on links. I’m not a fan because it obscures the element, so I turn it off for a nicer experience.

@layer base {
  html {
    -webkit-tap-highlight-color: transparent;
  }
}

@layer is a Tailwind directive. It helps avoid specificity issues by telling Tailwind which “bucket” contains a set of custom styles. It’s like pretending the cascade doesn’t exist, so there’s less to worry about when it comes to ordering CSS.

Simply removing the tap highlight color might trigger an accessibility issue since that hides an interactive cue. So, if you go this route, it’s probably a good idea (and I’m still looking for research on this if you have it) to enable :active to define provide some response to those actions. Chris has a snippet for that.

env(safe-area-inset-bottom)

This utility class handles the bottom bar on newer iPhones without the “Home” button. Without it, some elements can fall under the bar, making them unreadable and tough to tap.

@layer utilities {
  .pb-safe {
    padding-bottom: env(safe-area-inset-bottom);
  }
}

Interpuncts

I love using interpuncts with unordered lists. I won’t penalize you for looking that up. We’re basically talking about the bullet points in unordered lists. Tailwind removes them by default via Normalize. I smuggle interpuncts into each and every one of my projects.

Here’s how I go about it:

@layer utilities {
  .list-interpunct > li::before {
    content: '・';
    display: inline-block;
    float: left;
    margin: 0 0 0 -0.9em;
    width: 0.9em;
  }

  @media (min-width: 992px) {
   .list-interpunct > li::before {
      margin: 0 0 0 -1.5em;
      width: 1.5em;
    }
  }
}

We also now have ::marker to do the same thing and it’s a little easier to work with. I’m not using it, because of the limited support in Safari.

Now it’s your turn

Alright, I shared what I add to all of my Tailwind projects, so now it’s your turn. What do you add to Tailwind in your projects? Is there something you can’t do without? Let me know in the comments! I’d love ideas to start incorporating into other projects.


The post The Things I Add to Tailwind CSS Right Out of the Box appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

An Eleventy Starter with Tailwind CSS and Alpine.js

When I decided to try to base my current personal website on Eleventy, I didn’t want to reinvent the wheel: I tested all the Eleventy starters built with Tailwind CSS that I could find in Starter Projects from the documentation.

Many of the starters seemed to integrate Tailwind CSS in a contrived way. Also, some of them seemed to assume that no one updates Tailwind’s configuration on the fly while working on a website. That’s why I integrated Eleventy with Tailwind CSS and Alpine.js myself. I have reason to believe that you’ll like the simplicity of my solution.

Good design is as little design as possible.

—Dieter Rams, 10 Principles for Good Design

If you’re uninterested in the details, feel free to grab my starter and jump right in.

Getting started

I’m going to assume you have a general understanding of Tailwind CSS, HTML, JavaScript, Nunjucks, the command line, and npm.

Let’s start by with a new a folder, then cd to it in the command line, and initialize it with a package.json file:

npm init -y

Now we can install Eleventy and Tailwind CSS. We’ll throw in PostCSS as well:

npm install --save-dev @11ty/eleventy tailwindcss postcss-cli

We need to create a page to test whether we’ve successfully set things up. In a real use case, our pages will use templates, so we’ll do that here as well. That’s where Nunjucks fits into the mix, serving as a templating engine.

Let’s make a new file called index.njk in the project folder. We’ll designate it as the homepage:

{% extends "_includes/default.njk" %}


{% block title %}It does work{% endblock %}


{% block content %}
  <div class="fixed inset-0 flex justify-center items-center">
    <div>
      <span class="text-change">Good design</span><br/>
      <span class="change">is<br/>as little design<br/>as possible</span>
    </div>
  </div>
{% endblock %}

Basic templating

Now let’s create a new folder in the project folder called _includes (and yes, the folder name matters). Inside this new folder, we’ll create a file called default.njk that we’ll use as the default template for our layout. We’ll keep things simple with a basic HTML boilerplate:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>
      {% block title %}Does it work?{% endblock %}
    </title>
    <meta charset="UTF-8"/>
    {% if description %}
      <meta name="description" content="{{description}}"/>
    {% endif %}
    <meta http-equiv="x-ua-compatible" content="ie=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
    <link rel="stylesheet" href="/style.css?v={% version %}"/>
    {% block head %}{% endblock %}
  </head>
  <body>
    {% block content %}
      {{ content | safe }}
    {% endblock %}
  </body>
</html>

Configuring Tailwind CSS

Let’s take care of a test for Tailwind CSS in as few moves as possible. First, create a new subfolder called styles and a file in it called tailwind.config.js:

module.exports = {
  theme: {
    colors: {
      change: "transparent"
    }
  },
  variants: {},
  plugins: [],
}

Then, create a file called tailwind.css in that same styles folder:

/* purgecss start ignore */
@tailwind base;
/* purgecss end ignore */


.change {
  color: transparent;
}


/* purgecss start ignore */
@tailwind components;
/* purgecss end ignore */


@tailwind utilities;

We’re done with the styles folder for now. What we do need is a configuration file that tells PostCSS to use Tailwind CSS, which we can get by creating a new file in the root directory of the project folder called postcss.config.js. Here’s how we require Tailwind CSS and its configuration file with PostCSS:

module.exports = {
  plugins: [
    require(`tailwindcss`)(`./styles/tailwind.config.js`)
  ],
};

Starting and building the project

Now let’s create another new file in the same root directory called .gitignore. This will allow us to define what files to skip when committing the project to a repo, like on GitHub:

_site/
_tmp/
.DS_Store
node_modules/
package-lock.json

Next, is another new file, this time one that tells Eleventy what it can ignore, called .eleventyignore. It only needs one line:

node_modules

OK, now we will create a file called .eleventy.js (note the leading dot!) that basically configures Eleventy, telling it what files to watch and where to save its work:

module.exports = function (eleventyConfig) {
  eleventyConfig.setUseGitIgnore(false);


  eleventyConfig.addWatchTarget("./_tmp/style.css");


  eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" });


  eleventyConfig.addShortcode("version", function () {
    return String(Date.now());
  });
};

We can now update the package.json file with all of the scripts we need to start and build the site during development. The dependencies should already be there from the initial setup.

{
  "scripts": {
    "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch",
    "build": "ELEVENTY_PRODUCTION=true eleventy & ELEVENTY_PRODUCTION=true postcss styles/tailwind.css --o _site/style.css"
  },
  "devDependencies": {
    "@11ty/eleventy": "^0.11.0",
    "postcss-cli": "^7.1.0",
    "tailwindcss": "^1.4.6"
  }
}

Hey, great job! We made it. Build the project to generate the initial CSS — this step is only required the very first time we set up. From the command line:

npm run build

And — drumroll, please — let’s officially start the site:

npm run start

Open the page http://localhost:8080 in your browser. It’s not gonna look like much, but check out the page title in the browser tab:

It does work!

We can still do a little more checking to make sure everything’s good. Open up /styles/tailwind.config.js and change the transparent color value to something else, say black. Tailwind’s configuration should reload, along with the page in your browser.

Don’t lose sight of your browser and edit /styles/tailwind.css by changing transparent to black again. Your CSS file should reload and refresh in your browser.

Now we can work nicely with Eleventy and Tailwind CSS!

Optimizing the output

At this point, Tailwind CSS works with Eleventy, but the CSS file is huge. The generated HTML isn’t perfect either because it contains stuff like redundant newline characters. Let’s clean it up:

npm install --save-dev @fullhuman/postcss-purgecss postcss-clean html-minifier

Open up postcss.config.js and replace what’s in there with this:

module.exports = {
  plugins: [
    require(`tailwindcss`)(`./styles/tailwind.config.js`),
    require(`autoprefixer`),
    ...(process.env.ELEVENTY_PRODUCTION
      ? [
          require(`postcss-clean`),
          require(`@fullhuman/postcss-purgecss`)({
            content: ["_site/**/*.html"],
            defaultExtractor: (content) =>
              content.match(/[\w-/:]+(?<!:)/g) || [],
            whitelist: [],
            whitelistPatterns: [/body/, /headroom/, /ril/],
          }),
        ]
      : []),
  ],
};

In the future, in the process of creating your website, if something looks wrong after you build the project and you have the impression that fragments of CSS are missing, add “ignored” class names to the whitelist in the file above.

Add the following line to the beginning of the .eleventy.js file:

const htmlmin = require("html-minifier");

We also need to configure htmlmin in .eleventy.js as well:

eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
    if (
      process.env.ELEVENTY_PRODUCTION &&
      outputPath &&
      outputPath.endsWith(".html")
    ) {
      let minified = htmlmin.minify(content, {
        useShortDoctype: true,
        removeComments: true,
        collapseWhitespace: true,
      });
      return minified;
    }


    return content;
});

We’re using a transform here which is an Eleventy thing. Transforms can modify a template’s output. At this point, .eleventy.js should look like this:

const htmlmin = require("html-minifier");


module.exports = function (eleventyConfig) {
  eleventyConfig.setUseGitIgnore(false);


  eleventyConfig.addWatchTarget("./_tmp/style.css");


  eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" });


  eleventyConfig.addShortcode("version", function () {
    return String(Date.now());
  });


  eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
    if (
      process.env.ELEVENTY_PRODUCTION &&
      outputPath &&
      outputPath.endsWith(".html")
    ) {
      let minified = htmlmin.minify(content, {
        useShortDoctype: true,
        removeComments: true,
        collapseWhitespace: true,
      });
      return minified;
    }


    return content;
  });
};

Alright, let’s run npm run start once again. You’ll see that nothing has changed and that’s because optimization only happens during build. So, instead, let’s try npm run build and then look at the _site folder. There shouldn’t be a single unnecessary character in the index.html file. The same goes for the style.css file.

A project built like this is now ready to deploy. Good job! 🏆

Integrating Alpine.js

I decided to switch to Eleventy from Gatsby.js because it just felt like too much JavaScript to me. I’m more into the reasonable dose of vanilla JavaScript mixed with Alpine.js. We won’t get into the specifics of Alpine.js here, but it’s worth checking out Hugo DiFrancesco’s primer because it’s a perfect starting point.

Here’s how we can install it to our project from the command line:

npm install --save-dev alpinejs

Now we need to update .eleventy.js with this to the function that passes things through Alpine.js:

eleventyConfig.addPassthroughCopy({
  "./node_modules/alpinejs/dist/alpine.js": "./js/alpine.js",
});

Lastly, we’ll open up _includes/default.njk and add Alpine.js right before the closing </head> tag:

<script src="/js/alpine.js?v={% version %}"></script>

We can check if Alpine is working by adding this to index.njk:

{% extends "_includes/default.njk" %}


{% block title %}It does work{% endblock %}


{% block content %}
  <div class="fixed inset-0 flex justify-center items-center">
    <div>
      <span class="text-change">Good design</span><br/>
      <span class="change">is<br/>as little design<br/>as possible</span><br/>
      <span x-data="{message:'🤖 Hello World 🤓'}" x-text="message"></span>
    </div>
  </div>
{% endblock %}

Launch the project:

npm run start

If Alpine.js works, you’ll see “Hello World” in your browser. Congratulations, times two! 🏆🏆


I hope you can see how quick it can be to set up an Eleventy project, including integrations with Nunjucks for templating, Tailwind for styles and Alpine.js for scripts. I know working with new tech can be overwhelming and even confusing, so feel free to email me at csstricks@gregwolanski.com if you have problems starting up or have an idea for how to simplify this even further.

The post An Eleventy Starter with Tailwind CSS and Alpine.js appeared first on CSS-Tricks.