The Power of twin.macro
Tags: javascript, css, tailwindcss, webdev
Published: 2020-07-22 04:39:28

I'll explain what is twin.macro and I'll show you all of the features provided by twin.macro.

# What is **twin.macro**?

It's a library wich basically converts your TailwindCSS classes into CSS objects and shares them with emotion or styled-components to give you the power of writing with Styled Components.

# Features

First, we need to know that twin.macro works with emotion or styled-component.

### *Support*
*twin.macro* it's available for Vanilla JavaScript, React.js (CRA), Gatsby, and Next.js. Vue.js it's only an experimental version.

### *Pluggins*
For the moment, twin.macro accepts certain plugins like:
- [TailwindUI](https://tailwindui.com/documentation)
- [Custom Forms](https://github.com/tailwindcss/custom-forms)
- [Aspect Ratio](https://github.com/webdna/tailwindcss-aspect-ratio)
- [Typography](https://github.com/benface/tailwindcss-typography)
- [Gradients](https://github.com/benface/tailwindcss-gradients)

### *Prop to JSX Elements*

You can pass the `tw` **prop** into JSX elements, a good way if you have an element without a lot of classes.

```javascript
/** @jsx jsx **/
import { jsx } from '@emotion/core';
import 'twin.macro';

export default function App() {
  return (
    <h1 tw="text-2xl text-blue-500 font-bold">Hello world</h1>
  );
}
```

> In emotion it's very important that you import the JSX to your component, without this your classes appear like **[object object]** and obviously we don't want that.

### *Nesting `tw` with `css` prop*

In this case, you can pass de `css` prop to a JSX Element in order to create conditional styles.

In the example, we have a variable called **isBold** and basically in the `css` prop check if the **isBold** is true. If it's true then we will have an element with `font-bold` class.

```javascript
/** @jsx jsx **/
import { jsx } from '@emotion/core';
import tw from 'twin.macro';

export default function App() {
  const isBold = true;
  return (
    <h1 css={[tw`text-3xl text-blue-500`, isBold && tw`font-bold`]}>Hello world</h1>
  );
}
```

### *Mixing SASS styles with the `css` import*

With the `css` import, we can mix SASS style with our TailwindCSS classes.

```javascript
/** @jsx jsx **/
import { jsx } from '@emotion/core';
import tw, { css } from 'twin.macro';

export default function App() {
  
  const myCustomStyles = css`
    ${tw`font-bold`}
    &:hover {
      font-weight: 500;
      ${tw`text-black`}
    }
  `;

  return (
    <h1 css={[tw`text-3xl text-blue-500`, myCustomStyles]}>Hello world</h1>
  );
}
```

### *Styled Components*

With the `tw` import we can create a Styled Component, good if you have elements that you repeat a lot.

```javascript
import React from 'react';
import tw from 'twin.macro';

const MyButton = tw.button`border-2 border-blue-500 px-4 py-2`;

export default function App() {

  return (
      <MyButton>Hello World!</MyButton>
  );
}
```

And maybe, you want to have a "base" style for a Styled Component, you can Clone and Edit an existing Styled Component.

```javascript
import React, { Fragment } from 'react';
import tw from 'twin.macro';

const MyButton = tw.button`border-2 border-blue-500 px-4 py-2`;
const MyPrimaryButton = tw(MyButton)`text-white bg-blue-500`; // Cloned Styled Component

export default function App() {

  return (
    <Fragment>
      <MyButton>Hello World!</MyButton>
      <MyPrimaryButton>My Second Hello World!</MyPrimaryButton>
    </Fragment>
  );
}
```

### *Styled Component - Conditional Styles*

Maybe you need a conditional style, with `styled` import we can do it. 

```javascript
import React from 'react';
import tw, { styled } from 'twin.macro';

const MyButton = styled.button(({isBold, isPrimary}) => [
  tw`mt-5 ml-5 border-2 border-blue-500 px-4 py-2`,
  // Ternary
  isBold ? tw`font-bold` : tw`font-semibold`,
  // Conditional Style
  isPrimary && tw`text-white bg-blue-500`
]);


export default function App() {

  return (  
    <MyButton isPrimary>Hello World!</MyButton>
  );
}

```

In this Styled Component, you can create conditionals styles, passing props to the function in this case we have two "isBold" and "isPrimary". We can use the ternary operator to apply certain classes or styles depending on what we need.

### *Variant Groups*

One of twin.macro's new enhancements is the ability to group classes, which I really loved.

Maybe you're working in the Responsive web desing or in multiple classes or styles for the hover pseudo-class.

So, twin.macro allows you to group multiple classes, for example you have the following classes in your Styled Component: 

```html
<h1 tw="text-blue-500 bg-blue-500 border-2 border-blue-500 hover:text-blue-900 hover:bg-blue-900 hover:border-blue-500" >Hello World</h1>
```

Maybe you don't want to re-write `hover:` prefix to all the classes, now in twin.macro you can do the following:

```html
<h1 tw="text-blue-500 bg-blue-500 hover:(text-blue-900 bg-blue-900)">Hello World</h1>
```

Do you see it? You only need a single hover:() to add multiple styles that will react to the pseudo-element.

[See all variants to Prefix your Classes](https://github.com/ben-rogerson/twin.macro/blob/master/src/config/variantConfig.js)

### *Theme*

If you have a custom tailwind.config.js file, you can use our custom values of this file with the `theme` import available in twin.macro.

> twin.macro only uses the `theme` and `plugins` variables available in tailwind.config.js

**Example**

```javascript
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        electric: '#db00ff',
        ribbon: '#0047ff'
      }
    }
  },
  plugins: []
}
```

So, we have our tailwind.config.js with custom variables, to use it, we need to import the `theme` in our application.

```javascript
// React.js App Example
/** @jsx jsx **/
import { jsx } from '@emotion/core';
import tw, { css, theme } from 'twin.macro'  

const App = () => (
  <div
    css={[
      tw`flex flex-col items-center justify-center h-screen`,
      css({
        // Grab values from your config with the theme import
        background: `linear-gradient(
          ${theme`colors.electric`},
          ${theme`colors.ribbon`}
        )`
      })
    ]}
  >
    <h1>Hello World!</h1>
  </div>
)

export default App;

```

So, as you can see we create a custom linear-gradient using the custom colors that we add to tailwind.config.js. ``${theme`color.electric`}``

> In some cases you need to import and use the **jsx** import for emotion to use the prop CSS in JSX elements. 
``/** @jsx jsx **/ import { jsx } from '@emotion/core'; ``


### *You don't see the error?*

Maybe you don't remember the TailwindCSS class that you want, maybe looks like ml-45 or ml-73?

twin.macro has a helpful suggestion if you write a wrong class twin.macro will show you something like that:


```
✕ ml-7 was not found

Try one of these classes:
ml-0 [0] / ml-1 [0.25rem] / ml-2 [0.5rem] / ml-3 [0.75rem] / ml-4 [1rem] / ml-5 [1.25rem] / ml-6 [1.5rem]
ml-8 [2rem] / ml-10 [2.5rem] / ml-12 [3rem] / ml-16 [4rem] / ml-20 [5rem] / ml-24 [6rem] / ml-32 [8rem]
ml-40 [10rem] / ml-48 [12rem] / ml-56 [14rem] / ml-64 [16rem] / ml-auto [auto] / ml-px [1px]
```

In order to help you remember and write the correct class you need.

## Resources

- [twin.macro](https://github.com/ben-rogerson/twin.macro)
- [cra-template-tailwindcss-emotion](https://www.npmjs.com/package/cra-template-tailwindcss-emotion)