Integrating Tailwind CSS and Bit for Component Styling

ig
ignacioaldama1 year ago

In recent years, Tailwind CSS has gained immense popularity as a utility-first CSS framework for rapidly building custom user interfaces. This powerful tool simplifies the process of creating your own design system and is incredibly user-friendly.

In this article, we'll explore how we integrated Tailwind CSS (version 3.0) with Bit and discuss the challenges we faced along the way.

This article uses React as the example, but the same concepts apply to any other framework.

To learn how to customize the Tailwind CSS configurations for your components and apps, see this blog post

Add Tailwind CSS support to your components

Component previews

A component preview is an isolated rendering of a component. It's a great way to test your components in different contexts and variations, showcase them, and document them.

To provide your component previews with Tailwind support, set your env with the Tailwind transformer.

Change the Webpack configuration of your previews

Run the following to install the Tailwind webpack transformer in your workspace:

$bit
Copiedcopy
See command synopsis

Head over to your env's *.env.ts file, implement the preview service, and add the Tailwind webpack transformer to it:

/* @filename: react-tailwind.env.ts */

// ...
import { Preview } from '@teambit/preview';
import { tailwindTransformer } from '@learnbit/styling.transformers.tailwind';

export class ReactTailwindEnv extends ReactEnv {
  name = 'react-tailwind-env';

  /* customize the component preview to include tailwind */
  preview(): EnvHandler<Preview> {
    return ReactPreview.from({
      mounter: require.resolve('./preview/mounter'),
      transformers: [
        tailwindTransformer({}),
      ],
    });
  }
}

export default new ReactTailwindEnv();
CopiedCopy

See full example of a React env here.

If you don't plan to change the default Tailwind configurations, it is recommended to use Tailwind's CDN. To Inject the CDN into your component previews, set the Tailwind Webpack Transformer like so:

tailwindTransformer({
  cdn: true
})
CopiedCopy

Using the CDN reduces the build time of your components, and simplifies the Tailwind configuration process as no further steps are required.

Add the Tailwind main file to your component compositions

This step is only needed if you don't use Tailwind's CDN (see previous step)!

Tailwind requires a main file to be loaded in order to generate the CSS classes. Since each component preview is isolated, it needs to load the main file, as well.

Run the following to install the Tailwind config component which includes the main file:

$bit
Copiedcopy
See command synopsis

Fork this component to change the default Tailwind configuration and main file.

Import the Tailwind main file into each of your component composition files.

For example, the following is a React component compositions file that loads the Tailwind main file:

/* @filename: menu.compositions.tsx */

import { Button } from './button';
/* load the tailwind main file */
import '@learnbit/styling.config.tailwind/globals.tailwind.css';

export const BasicButton = () => {
  return <Button type="button">Hello World</Button>;
};
CopiedCopy

See a full example here.

Use component templates to add the Tailwind main file to your compositions by default

We recommend adding the Tailwind CSS main file to your component templates, so that it will be included, by default, in the composition files of components generated from the template.

Verify your previews use Tailwind CSS

Run the following to start your workspace and see the Tailwind styles applied to your components' previews:

$bit
Copiedcopy

For example, the following is a single preview of a button component. Click on the code tab to see the button.composition.tsx composition file, which is loaded by the preview:

Component apps

To bundle your React app components with Tailwind styles, set your app component's plugin file with the Tailwind transformer.

Change the Webpack configuration of your app component

Install the the transformer into your workspace:

$bit
Copiedcopy
See command synopsis

Set your app component's plugin file with the Tailwind transformer:

/* tailwind-demo.react-18-app.tsx */

const {
  tailwindTransformer,
} = require('@learnbit/styling.transformers.tailwind');

/** @type {import("@teambit/react.apps.react-app-types").ReactAppType} */
module.exports.default = {
  name: 'tailwind-demo',
  entry: [require.resolve('./demo-app.app-root')],
  webpackTransformers: [tailwindTransformer()],
};
CopiedCopy

Add the Tailwind main file to your app root file

Add the Tailwind main file to your app's root file. For example:

/* @filename: demo-app.app-root.tsx */

import { createRoot } from 'react-dom/client';
import { DemoApp } from './app';
import '@learnbit-react/tailwind.tailwind-config/globals.tailwind.css';

const container = document.getElementById('root');
const root = createRoot(container!);
root.render(<DemoApp />);
CopiedCopy

See full example here.

Verify your app uses Tailwind CSS

Run the following with your app's name, to run it locally:

$bit
Copiedcopy
See command synopsis

For example:

Let's take a closer look at how it works

The Tailwind Webpack transformer

The Tailwind webpack transformer does the following to add Tailwind CSS support to your components:

  1. Since it is a webpack transformer, it receives a Webpack config and updates it, accordingly. In this case, it adds the Tailwind Webpack PostCSS plugin to the env's preview Webpack configuration.
  2. The Tailwind PostCSS plugin scans the component's files, looking for Tailwind classes (to generate). It does that by receiving a list of file patterns to scan, in this case the *.tsx and *.jsx files of components in the workspace.
    1. Since components are consumed and executed using the package generated for them.
/* @filename: tailwind-transformer.ts */

//...
import type { WebpackConfigMutator } from '@teambit/webpack';
import tailwindcss, { Config } from 'tailwindcss';
import { tailwindConfig } from '@learnbit/styling.transformers.tailwind';

export function tailwindTransformer({
  cdn,
  config,
}: TailwindTransformerOptions = {}) {
  const usedConfig = config || tailwindConfig;

  return (configMutator: WebpackConfigMutator) => {
    configMutator.addPostCssPlugins([tailwindcss(usedConfig)]);

    if (cdn) {
      configMutator.addElementToHtmlTemplate({
        parent: 'head',
        position: 'append',
        tag: 'script',
        attributes: {
          src: 'https://cdn.tailwindcss.com/',
        },
      });

      configMutator.addElementToHtmlTemplate({
        parent: 'head',
        position: 'append',
        tag: 'script',
        content: `tailwind.config = ${JSON.stringify(usedConfig, null, 2)}`,
      });
    }

    return configMutator;
  };
}
CopiedCopy

Live playground support

Lastly, since the component previews and documentation are also rendered in the components' remote scopes, we decided to inject to the preview's HTML page, the Tailwind CDN, which scans the page and generates Tailwind classes accordingly, in runtime. That allows your teammates to play with your Tailwind components' styles, in their scope, without having to rebuild the components.

For example, the following uses the Tailwind CDN. Insert a Tailwind class to see it rendered live. For example, try to enter bg-blue-700:

See the button component page to play with the playground embedded in the component's documentation.