DEV Community

Cover image for The Power of twin.macro
Angel Martinez
Angel Martinez

Posted on

The Power of twin.macro

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:

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.

/** @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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

/** @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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Mixing SASS styles with the css import

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

/** @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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Styled Components

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

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Styled Component - Conditional Styles

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

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

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:

<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>
Enter fullscreen mode Exit fullscreen mode

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

<h1 tw="text-blue-500 bg-blue-500 hover:(text-blue-900 bg-blue-900)">Hello World</h1>
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

// 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;

Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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

Resources

Top comments (4)

Collapse
 
frontend_io profile image
Jefferson Osagie Iyobosa

Nice article.
I've been trying to get twin macro to work with my Next JS App, I can't seem to figure it out. Is there a resource you could recommend. Meanwhile I've used this before with CRA.

Collapse
 
angelmtztrc profile image
Angel Martinez

You can read the following post:
github.com/ben-rogerson/twin.macro...

Collapse
 
okeken profile image
okeken

you might want to check out my template here, it's using nextjs, i'm using rtk query for the state managment.

github.com/okeken/nextjs-rtk-query

Collapse
 
ravinn profile image
ravinn

Hi. Very good writeup. Can twin.macro be used with lit-element?